DEV Community

Billy Chan
Billy Chan

Posted on

6

The Question Mark `?` Operator in Rust

The question mark ? operator is used for early exit an function with return type that's compatible with the value of the ? is used on. Such as Err(e) but not many people know it can be used on None as well.

The ? operator is commonly used with Result<T, E> to propagate error up the call chain. We can define a unified error enum to host all errors. Remember to take advantage of Rust's conversion method From<T> to cast foreign errors into the unified error enum. That way, we take advantage of ? operator, it will perform the conversion behind the scene.

// Unified `Error` enum
#[derive(Debug)]
enum Error {
    ParseIntError(std::num::ParseIntError),
    ParseFloatError(std::num::ParseFloatError),
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ParseIntError(e) => write!(f, "ParseIntError: {}", e.to_string()),
            Self::ParseFloatError(e) => write!(f, "ParseFloatError: {}", e.to_string()),
        }
    }
}

impl std::error::Error for Error {}

// Convert `std::num::ParseIntError` to `Error`
impl From<std::num::ParseIntError> for Error {
    fn from(err: std::num::ParseIntError) -> Self {
        Self::ParseIntError(err)
    }
}

// Convert `std::num::ParseFloatError` to `Error`
impl From<std::num::ParseFloatError> for Error {
    fn from(err: std::num::ParseFloatError) -> Self {
        Self::ParseFloatError(err)
    }
}

fn main() -> Result<(), Error> {
    // Parse an integer and unwrap it, or throw `Error::ParseIntError`
    let _: i32 = "123".parse()?;
    let _: i32 = "not_an_integer".parse()?;

    // Parse a float and unwrap it, or throw `Error::ParseFloatError`
    let _: f64 = "not_a_number".parse()?;

    Ok(())
}

Enter fullscreen mode Exit fullscreen mode

The ? operator could also perform early return on Option<T> return type.

fn get_first_char_of_second_word(phrase: &str) -> Option<char> {
    // Return `None` if the phrase consist of a single word
    phrase.split(" ").skip(1).next()?.chars().next()
}

fn main() {
    assert_eq!(get_first_char_of_second_word("Hello World"), Some('W'));
    assert_eq!(get_first_char_of_second_word("Hello?"), None);
}
Enter fullscreen mode Exit fullscreen mode

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay