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:?}"); }
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
.