Sobre o que é esse operador de ponto de interrogação?

Respostas:

149

Como você deve ter notado, Rust não tem exceções. Há pânico, mas sua funcionalidade é limitada (eles não podem transportar informações estruturadas) e seu uso para tratamento de erros é desencorajado (eles são feitos para erros irrecuperáveis).

Em Rust, usa o tratamento de erros Result. Um exemplo típico seria:

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`
}

Isso é ótimo porque:

  • ao escrever o código, você não pode esquecer acidentalmente de lidar com o erro,
  • ao ler o código, você pode ver imediatamente que existe um potencial de erro aqui.

É menos do que ideal, no entanto, por ser muito prolixo. É aqui que o operador ponto de interrogação ?entra.

O texto acima pode ser reescrito como:

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

    // use `i`
}

que é muito mais conciso.

O que ?significa aqui é equivalente à matchafirmação acima. Resumindo: ele descompacta o Resultif OK e retorna o erro se não.

É um pouco mágico, mas o tratamento de erros precisa de um pouco de mágica para reduzir o clichê e, ao contrário das exceções, é imediatamente visível quais chamadas de função podem ou não dar erro: aquelas que são adornadas com ?.

Um exemplo da mágica é que isso também 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`
}

Isso é alimentado pelo Trytraço (instável) .

Veja também:

Matthieu M.
fonte
5
seria bom se você pudesse estender sua resposta um pouco, por exemplo, discutir que o tipo de retorno da função deve corresponder ao tipo que você tenta "desembrulhar", por exemplo, Resultou Option.
hellow
@hellow, acho melhor que seja uma pergunta totalmente nova
Paul Razvan Berg,
2

É para propagação de erro para o tipo de erro recuperável Resultado <T, E>. Ele desembrulha o resultado e lhe dá o valor interno.

Em vez de lidar com o caso de erro, você o propaga para o código do chamador e trata apenas do caso OK. A vantagem é que ele elimina muitos clichês e torna a implementação da função mais simples.

snnsnn
fonte
1
não deve ser confundido com o real .unwrap()que entra em pânico em caso de erro.
Jordan,