¿De qué se trata este operador de signo de interrogación?

Respuestas:

145

Como habrás notado, Rust no tiene excepciones. Tiene pánico, pero su funcionalidad es limitada (no pueden transportar información estructurada) y se desaconseja su uso para el manejo de errores (están pensados ​​para errores irrecuperables).

En Rust, el manejo de errores utiliza Result. Un ejemplo típico sería:

fn halves_if_even(i: i32) -> Result<i32, Error> {
    if i % 2 == 0 {
        Ok(i / 2)
    } else {
        Err(/* something */)
    }
}

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = match halves_if_even(i) {
        Ok(i) => i,
        Err(e) => return Err(e),
    };

    // use `i`
}

Esto es genial porque:

  • al escribir el código, no puede olvidarse accidentalmente de tratar el error,
  • al leer el código, puede ver inmediatamente que existe un potencial de error aquí.

Sin embargo, es menos que ideal, ya que es muy detallado. Aquí es donde ?entra el operador del signo de interrogación .

Lo anterior se puede reescribir como:

fn do_the_thing(i: i32) -> Result<i32, Error> {
    let i = halves_if_even(i)?;

    // use `i`
}

que es mucho más conciso.

Lo que ?hace aquí es equivalente a la matchdeclaración anterior. En resumen: descomprime el Resultif OK y devuelve el error si no.

Es un poco mágico, pero el manejo de errores necesita algo de magia para reducir el estándar y, a diferencia de las excepciones, es inmediatamente visible qué llamadas de función pueden o no fallar: aquellas que están adornadas ?.

Un ejemplo de la magia es que esto también funciona para Option:

// Assume
// fn halves_if_even(i: i32) -> Option<i32>

fn do_the_thing(i: i32) -> Option<i32> {
    let i = halves_if_even(i)?;

    // use `i`
}

Esto es impulsado por el Tryrasgo (inestable) .

Ver también:

Matthieu M.
fuente
5
Sería bueno si pudiera extender su respuesta un poco, por ejemplo, discutir que el tipo de retorno de la función debe coincidir con el tipo que intenta "desenvolver", por ejemplo, Resulto Option.
hola
@hellow Supongo que será mejor que sea una pregunta completamente nueva
Paul Razvan Berg
2

Es para la propagación de errores para el tipo de error recuperable Resultado <T, E>. Desenvuelve el resultado y te da el valor interior.

En lugar de manejar el caso de error, lo propaga al código de la persona que llama y trata solo el caso Ok. El beneficio es que elimina una gran cantidad de repetición y simplifica la implementación de la función.

snnsnn
fuente
no confundir con el actual .unwrap()que entra en pánico en caso de error.
Jordania