Conversiones Implícitas

La expansión efectiva de ? es un poco más complicada de lo que se ha indicado anteriormente:

expression?

funciona igual que

match expression {
    Ok(value) => value,
    Err(err)  => return Err(From::from(err)),
}

The From::from call here means we attempt to convert the error type to the type returned by the function. This makes it easy to encapsulate errors into higher-level errors.

Ejemplo

use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::fs::File;
use std::io::{self, Read};

#[derive(Debug)]
enum ReadUsernameError {
    IoError(io::Error),
    EmptyUsername(String),
}

impl Error for ReadUsernameError {}

impl Display for ReadUsernameError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            Self::IoError(e) => write!(f, "Error IO: {e}"),
            Self::EmptyUsername(path) => write!(f, "No se ha encontrado ningún nombre de usuario en {path}"),
        }
    }
}

impl From<io::Error> for ReadUsernameError {
    fn from(err: io::Error) -> Self {
        Self::IoError(err)
    }
}

fn read_username(path: &str) -> Result<String, ReadUsernameError> {
    let mut username = String::with_capacity(100);
    File::open(path)?.read_to_string(&mut username)?;
    if username.is_empty() {
        return Err(ReadUsernameError::EmptyUsername(String::from(path)));
    }
    Ok(username)
}

fn main() {
    //fs::write("config.dat", "").unwrap();
    let username = read_username("config.dat");
    println!("nombre de usuario o error: {username:?}");
}
This slide should take about 5 minutes.

El operador ? debe devolver un valor compatible con el tipo de resultado devuelto de la función. En Result, significa que los tipos de error deben ser compatibles. Una función que devuelve Result<T, ErrorOuter> solo puede usar ? en un valor del tipo Result<U, ErrorInner> si ErrorOuter y ErrorInner son del mismo tipo o si ErrorOuter implementa . From<ErrorInner>.

Una alternativa habitual a la implementación From es Result::map_err, sobre todo si la conversión solo se produce en un lugar.

No hay ningún requisito de compatibilidad para Option. Una función que devuelve Option<T> puede usar el operador ? en Option<U> para tipos arbitrarios de T y U.

Una función que devuelve Result no puede usar ? en Option y viceversa. Sin embargo, Option::ok_or convierte Option en Result, mientras que Result::ok convierte Result en Option.