Option<T> and Result<T, E> in Depth
with Result
Applying the ? operator to a Result<T, E> inside a function or closure returning Result<U, F> unwraps Ok(v) into v, or converts Err(e) via From::from(e) and returns Err(F) early from the enclosing function.
Desugaring and Automatic From Conversion
let val = fallible_op()?;
// Desugars conceptually to:
let val = match fallible_op() {
Ok(v) => v,
Err(e) => return Err(core::convert::From::from(e)),
};
Because the Err(e) branch invokes From::from(e), a single function returning a unified domain error Result<T, ServiceError> can use ? across std::io::Error, std::num::ParseIntError, and std::str::Utf8Error seamlessly as long as ServiceError implements From for each underlying error type:
use std::num::ParseIntError;
use std::str::{self, Utf8Error};
#[derive(Debug)]
enum PacketError {
InvalidUtf8(Utf8Error),
InvalidNumber(ParseIntError),
}
impl From<Utf8Error> for PacketError {
fn from(e: Utf8Error) -> Self {
PacketError::InvalidUtf8(e)
}
}
impl From<ParseIntError> for PacketError {
fn from(e: ParseIntError) -> Self {
PacketError::InvalidNumber(e)
}
}
fn decode_ascii_counter(raw_bytes: &[u8]) -> Result<u64, PacketError> {
let text = str::from_utf8(raw_bytes)?; // Converts Utf8Error -> PacketError
let number = text.trim().parse::<u64>()?; // Converts ParseIntError -> PacketError
Ok(number)
}
fn main() {
println!("Valid bytes: {:?}", decode_ascii_counter(b" 1024 "));
println!("Invalid UTF-8: {:?}", decode_ascii_counter(&[0xFF, 0xFE]));
println!("Invalid int: {:?}", decode_ascii_counter(b"not_an_int"));
}
Valid bytes: Ok(1024)
Invalid UTF-8: Err(InvalidUtf8(Utf8Error { valid_up_to: 0, error_len: Some(1) }))
Invalid int: Err(InvalidNumber(ParseIntError { kind: InvalidDigit }))
Using ? in main() and Unit Tests
Both fn main() and #[test] functions can return Result<(), E> (where E: std::fmt::Debug, such as Result<(), Box<dyn std::error::Error>>), allowing you to use ? directly at the top level of binaries and tests instead of .unwrap():
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let port: u16 = "8080".parse()?;
println!("Configured port: {port}");
Ok(())
}
Configured port: 8080