Pybites Logo Rust Platform

The ? Operator

Medium +3 pts

Chaining .and_then() works, but gets verbose. Rust's ? operator is syntactic sugar for error propagation.

Before: manual matching

fn read_config() -> Result<Config, Error> {
    let file = match File::open("config.txt") {
        Ok(f) => f,
        Err(e) => return Err(e),
    };
    let contents = match read_to_string(file) {
        Ok(s) => s,
        Err(e) => return Err(e),
    };
    parse_config(&contents)
}

After: with ?

fn read_config() -> Result<Config, Error> {
    let file = File::open("config.txt")?;
    let contents = read_to_string(file)?;
    parse_config(&contents)
}

The ? does exactly what the match did: if Ok, unwrap the value; if Err, return early with the error.

Python comparison

In Python, exceptions propagate automatically:

def read_config():
    file = open("config.txt")  # raises if fails
    contents = file.read()      # raises if fails
    return parse_config(contents)

Rust's ? gives you the same concise code, but the error handling is explicit in the return type.

? works with Option too

fn get_first_char(s: Option<String>) -> Option<char> {
    let text = s?;  // return None if s is None
    text.chars().next()
}

The return type must match

? only works when the function returns Result or Option. This won't compile:

fn main() {
    let f = File::open("test.txt")?;  // ERROR: main returns ()
}

Fix: use .unwrap(), .expect(), or change the return type.

Error conversion with From

If your function returns Result<T, MyError> but you call something that returns Result<T, OtherError>, Rust can convert automatically if MyError: From<OtherError>:

impl From<io::Error> for MyError {
    fn from(e: io::Error) -> Self {
        MyError::Io(e)
    }
}

Login to see the full task and start coding.

This is a premium exercise

Log in to unlock the full exercise and start coding.

Login to access this exercise