Aprendendo Rust: Option vs Null

Descubra como lidar de maneira segura com tipos nuláveis

É muito comum na programação que tenhamos o problema de controlar quando um valor pode existir ou pode não existir, se você requisita o primeiro item de uma lista não vazia receberá um valor, entretanto, se você pedir o primeiro item de uma lista vazia não receberá nada, isso implica que qualquer valor sempre terá dois estados nulo e não-nulo.

Este post é um resumo traduzido dos conceitos que você pode encontrar no livro, que é gratuito: The Rust Programming Language

Em Rust, uma característica da linguagem é não adotar o tipo null, isso se faz por conta da baixa confiabilidade sobre a implementação do tipo null, como diz o próprio criador do tipo na palestra “Null References The Billion Dollar Mistake”. Sendo assim como a linguagem gerência a falta ou a presença de um mesmo valor?

Option

Uma implementação particular, onde para cada valor nulo ou não nulo eu tenho a representação deste em Option, que recebe um tipo T qualquer, e pode retornar Some(T) para representar a presença de um dado tipo T ou None para representar a falta do mesmo dado.

enum Option<T> {
    None,
    Some(T),
}

Por ser uma abstração que pertence a linguagem você não precisa fazer a chamada padrão para um enum, podendo invocar diretamente os tipos Some ou None.

O tipo Some consegue segurar dados de qualquer tipo e retorna um Option<T>, onde T é o tipo passado ao enum.

O tipo None sempre que for declarado precisa da referência de qual tipo seria sua representação não-nula, enquanto o mesmo não acontece para Some pois o compilador pode inferir seu tipo pelo valor usado.

Para tornar o Option uma implementação mais segura e eficiente que o null, existe um tratamento de tipos, por exemplo…

O tipo Some pode declarar um valor x e um tipo T entretanto caso eu queira somar uma variável y com o mesmo tipo T meu código não irá compilar pois Option<T> não é igual a T,

    let x: Option<i8> = Some(5);
    let y: i8 = 5;

    let sum = x + y;
    // Não compila pois tenta somar Option<i8> com i8

você precisará converter Option<T> para T antes de realizar qualquer operação possível para T.

    let x: Option<i8> = Some(5);
    let y: i8 = 5;

    let sum = x.unwrap() + y;
    // Compila pois transformou optional<i8> em i8

Isso nos ajuda a manter longe um dos principais problemas com null: Assumir que um valor não é nulo quando ele na verdade é.

Como extrair valores de um Option

na definição de Option pude explicar como o compilador entende que o valor é nulo ou não nulo e como isso se faz coerente em comparação a nível de código, entretanto não listamos as maneiras de extrair T ou None para usarmos.

Pattern matching

Quando se recebe uma Option qualquer a maneira mais intuitiva de saber quais das opções esta representação me entregou é fazendo um match

fn is_odd(number: i8) -> Option<i8> {
    if number % 2 == 0 {
        None
    } else {
        Some(number)
    }
}

let number = 17;
let result = is_odd(number);

match result {
    Some(x) => println!("{} is odd", x),
    None    => println!("isn't odd"),
}

No exemplo acima usamos a função is_odd para definirmos nossa Option onde caso o número não fosse impar o retorno seria None ou como seria em outras linguagens null.

Métodos de Options

Existem métodos implementados em Rust onde esperam extrair um valor caso exista Some e também disparam tipos de erros diferentes baseado nos métodos, você pode acessar a documentação completa mas vou listar alguns aqui abaixo.

  • expect gera panic com uma mensagem personalizada fornecida.

  • unwrap gera panics com uma mensagem genérica.

  • unwrap_or retorna o valor default fornecido.

  • unwrap_or_default retorna o valor default do tipo T.

  • unwrap_or_else retorna o resultado da avaliação da função fornecida.

Esse é um tema bem complexo mas você pode encontrar mais referências nos links indexados no texto e também, caso se interesse, pode olhar ainda mais sobre o fundamento do tipo Option nesses links

Definição de Monadas

API do Rust sobre Option

Vejo vocês no próximo post pessoal 😁