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() {
//std::fs::write("config.dat", "").unwrap();
let username = read_username("config.dat");
println!("username ou erro: {username:?}");
}
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.