Otros usos de ?

Observa en el ejemplo anterior que nuestra reacción inmediata al llamar parse es mapear el error de un error de biblioteca a un error encuadrado:

.and_then(|s| s.parse::<i32>()
    .map_err(|e| e.into())

Dado que se trata de una operación simple y común, sería conveniente que se pudiera elidir. Por desgracia, debido a que and_then no es lo suficientemente flexible, no se puede. Sin embargo, en su lugar podemos usar ?.

? se explicó anteriormente como unwrap o return Err(err). Esto solo es cierto en su mayor parte. En realidad, significa unwrap o return Err(From::from(err)). Dado que From::from es una utilidad de conversión entre diferentes tipos, esto significa que si usas ? donde el error es convertible al tipo de retorno, se convertirá automáticamente.

Aquí, reescribimos el ejemplo anterior usando ?. Como resultado, el map_err desaparecerá cuando se implemente From::from para nuestro tipo de error:

use std::error;
use std::fmt;

// Cambie el alias a `Box<dyn error::Error>`.
type Result<T> = std::result::Result<T, Box<dyn error::Error>>;

#[derive(Debug)]
struct VecVacio;

impl fmt::Display for VecVacio {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "primer elemento inválido para duplicar")
    }
}

impl error::Error for VecVacio {}

// La misma estructura que antes pero en lugar de encadenar todos los `Result` y `Option`
// a lo largo, nosotros usamos `?` para obtener el valor interno inmediatamente.

fn duplica_primer_elemento(vec: Vec<&str>) -> Result<i32> {
    let first = vec.first().ok_or(VecVacio)?;
    let parsed = first.parse::<i32>()?;
    Ok(2 * parsed)
}

fn imprime(result: Result<i32>) {
    match result {
        Ok(n)  => println!("El doble del primer elemento es {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    let numeros = vec!["42", "93", "18"];
    let vacio = vec![];
    let cadenas = vec!["tofu", "93", "18"];

    imprime(duplica_primer_elemento(numeros));
    imprime(duplica_primer_elemento(vacio));
    imprime(duplica_primer_elemento(cadenas));
}

Esto está bastante limpio ahora. En comparación con el panic original, es muy similar a reemplazar las llamadas de unwrap por ? excepto que los tipos de retorno son Result. Como resultado, deben desestructurarse en el nivel superior.

Ve también

From::from y ?