ConversÔes Try

A expansĂŁo efetiva do operador ? Ă© um pouco mais complicada do que indicado anteriormente:

expression?

funciona da mesma forma que

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

A chamada From::from aqui significa que tentamos converter o tipo de erro para o tipo retornado pela função. Isso torna fåcil encapsular erros em erros de nível superior.

Exemplo

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, "Erro E/S: {e}"),
            Self::EmptyUsername(path) => write!(f, "Nome de usuĂĄrio nĂŁo encontrado em {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!("username ou erro: {username:?}");
}
This slide should take about 5 minutes.

O operador ? deve retornar um valor compatível com o tipo de retorno da função. Para Result, isso significa que os tipos de erro devem ser compatíveis. Uma função que retorna Result<T, ErrorOuter> só pode usar ? em um valor do tipo Result<U, ErrorInner> se ErrorOuter e ErrorInner forem do mesmo tipo ou se ErrorOuter implementar From<ErrorInner>.

Uma alternativa comum a uma implementação From é Result::map_err, especialmente quando a conversão ocorre apenas em um local.

Não hå requisito de compatibilidade para Option. Uma função que retorna Option<T> pode usar o operador ? em Option<U> para tipos T e U arbitrårios.

Uma função que retorna Result não pode usar ? em Option e vice-versa. No entanto, Option::ok_or converte Option em Result, enquanto Result::ok transforma Result em Option.