Aprendendo Rust do zero - Variáveis e Mutabilidade 🌟

Aprendendo Rust do zero - Variáveis e Mutabilidade 🌟

Como declarar variáveis e suas características

Introdução 📝

Esse post faz parte de uma série que estou escrevendo chamada "Aprendendo Rust do zero" onde eu tento resumir o que acho ser importante baseado no livro oficial no site do Rust. Nesse aqui gostaria de abordar variáveis e mutabilidade.

Sumário 📖

  1. Instalando Rust
  2. Hello, World!
  3. Hello, Cargo!
  4. Variáveis e Mutabilidade - você está aqui
  5. Tipos de dados
  6. Funções
  7. Comentários Simples
  8. Controle de Fluxo

Variáveis e Mutabilidade 🌟

Em Rust, diferente das outras linguagens de programação, as variáveis são imutáveis por padrão. Porém ainda há a possibilidade de criar variáveis mutáveis.

Quando uma variável é imutável, assim que um valor é passado pra ela, não é possível mais alterar esse valor. Para ilustrar vamos criar um novo projeto utilizando cargo (que você aprendeu no último post) através do comando cargo new variables.

Abra então o arquivo main.rs dentro do diretório variables/src e substitua o conteúdo pelo seguinte código (esse código não vai compilar ainda):

fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

Salve o código e execute o programa com o comando cargo run. Deve aparecer um erro, como mostrado abaixo:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         -
  |         |
  |         first assignment to `x`
  |         help: consider making this binding mutable: `mut x`
3 |     println!("The value of x is: {}", x);
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable

For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables` due to previous error

O exemplo acima mostra como o compilador ajuda você a encontrar erros nos seus programas. Mesmo que os erros sejam frustrantes, eles simplesmente significam que seu programa ainda não está fazendo corretamente o que você quer que ele faça. Não quer dizer que você não é um bom programador, programadores experiêntes também criam erros.

A mensagem de erro cannot assign twice to immutable variable 'x', significa que você não pode atribuir dois valores para uma mesma variável imutável. Como podemos ver no exemplo, primeiro a variável (que é imutável por padrão) x recebe o valor 5 e depois tentamos atribuir o valor 6, o que causa o erro.

É importante ver erros em tempo de compilação ao tentar mudar um valor que foi definido como imutável, pois situações assim podem gerar bugs. Se uma parte do nosso código funciona de uma forma assumindo que um valor nunca vai mudar, e outra parte do código muda esse valor, é possível que a primeira parte não faça o que foi designada a fazer. A causa desse tipo de bug pode ser difícil de rastrear, especialmente quando a segunda parte do código nem sempre vai mudar o valor imutável.

Em Rust, o compilador garante que quando você define que um valor não vai mudar, esse valor realmente não mude. Isso significa que quando você estiver lendo e escrevendo código, você não precisa ficar lembrando de como e onde um valor pode mudar. Isso faz com que seu código fique fácil de assimilar.

Porém mutabilidade pode ser muito útil. Variáveis são imutáveis somente por padrão. Para torna-lá mutável basta adicionar a palavra reservada mut antes do nome da variável. Adicionar mut na variável torna explícito a intenção futura de alteração do valor da variável.

Por exemplo, vamos mudar o arquivo src/main.rs conforme o exemplo a seguir:

fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

Ao executar o programa agora, recebemos isso:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.30s
     Running `target/debug/variables`
The value of x is: 5
The value of x is: 6

Foi possível alterar o valor da variável x de 5 para 6 pois adicionamos mut na declaração da variável. Em algumas situações tornar a variável mutável vai tornar mais conveniente escrever o código em vez de ficar usando somente variáveis imutáveis.

Diferença entre Variáveis e Constantes 💾

Ser impedido de mudar o valor de uma variável talvez tenha te lembrado de outro conceito de programação que a maioria das outras linguagens tem: constantes. Assim como variáveis imutáveis, constantes são valores que são atribuidos a um nome e não podem mudar, mas existem algumas diferenças entre constantes e variáveis em Rust.

  1. Você não pode utilizar mut com constantes. Constantes não são simplesmente imutáveis por padrão, elas são sempre imutáveis.

  2. Você dclara constantes com a palavra reservada const em vez de usar let, e o tipo do valor deve ser definido.

  3. Constantes podem ser declaradas em qualquer escopo, incluíndo o escopo global, o que torna elas úteis para valores que muitas partes do código precisam utilizar.

  4. Constantes só podem ser atribuídas a expressões constantes, não o resultado de um valor que só pode ser computado em tempo de execução.

A seguir podemos ver um exemplo de declaração de uma constante onde o nome dela é THREE_HOURS_IN_SECONDS e o seu valor é definido através do resultado da multiplicação de 60 ( a quantidade de segundos em um minuto) por 60 (a quantidade de minutos em uma hora) por 3 (a quantidade horas que queremos contar):

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

A convenção para nomear constantes em Rust é usar letras maiúsculas em todas as letras e separar as palavras com underline (ou underscores em inglês).

Constantes são válidas durante todo o tempo em que um programa é executado, com o escopo em que elas foram declaradas. Essa propriedade faz com que as constantes sejam úteis para valores que múltiplas partes do programa precisam saber, como por exemplo, a quantidade máxima de pontos que um jogador pode ganhar em um jogo ou a velocidade da luz.

Definir valores direto no código como constantes é uma forma conveniente de definir o significado daquele valor para futuros programadores que venham a revisar ou trabalhar com seu código. Isso também ajuda a ter um lugar único no seu código onde será preciso mudar os valores caso seja necessário no futuro.

Fazendo Shadowing em variáveis 👤

No Rust é possível declarar uma nova variável com o mesmo nome de uma variável declarada previamente. A comunidade do Rust costuma chamar isso de shadowing. Shadow em inglês significa sombra, então shadowing seria a ação, algo como se fosse fazer sombra (eu quis manter o termo em inglês de propósito pois caso seja necesário uma busca pela internet você, querida(o) leitor, já sabe do que se trata 😉). Continuando, quando uma variável é shadowed por outra, significa que o programa vai ler e manter a última declaração daquela váriavel quando ela for usada. Para fazer o shadowing corretamente é necessário utilizar o mesmo nome da primeira variável que você deseja fazer a sombra, e utilizar a palavra reservada let, como pode ser visto a seguir no arquivo src/main.rs:

fn main() {
    let x = 5;

    let x = x + 1;

    {
        let x = x * 2;
        println!("The value of x in the inner scope is: {}", x);
    }

    println!("The value of x is: {}", x);
}

Esse código primeiro atribui o valor 5 à variável x. Depois faz um shadow em cima da variável x repetindo let x = pegando o valor original e somando 1, então o valor de x passa a ser 6. Depois, dentro de um escopo interno, o terceiro let x = também faz um shadow na variável x, multiplicando o valor prévio por 2, resultando em x sendo igual a 12. Quando aquele escopo acaba, o shadow daquele escopo também acaba e a variável x valta a ser igual a 6. Se executarmos esse programa, teremos o seguinte resultado:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/variables`
The value of x in the inner scope is: 12
The value of x is: 6

Fazer shadowing é diferente de declarar a variável utilizando mut, pois ao fazer a sombra, teremos um erro na hora de compilar se tentarmos acidentalmente re-atribuir um valor a essa variável sem usar let. Ao usar a palavra let podemos fazer algumas transformações no valor dessa variável e continuar com a imutabilidade depois que essas transformações ocorram.

A outra diferença entre mut e shadowing, é que como estamos criando uma nova variable quando usamos let novamente, nós podemos mudar o tipo do valor mas reutilizar o mesmo nome. Por exemplo, digamos que nosso programa pede ao usuário para definir quantos espaços ele quer entre um texto utilizando caracteres de espaço em branco, mas nós queremos mesmo é salvar aquele valor como um número:

    let spaces = "   ";
    let spaces = spaces.len();

Essa utilização é correta pois a primeira declaração da variável spaces é uma string e a segunda declaração de spaces, que é uma nova variável que tem o mesmo nome que a primeira, é do tipo number. Fazer o shadowing nos poupa de de ter que utilizar nomes diferentes, como por exemplo spaces_str e spaces_num, em vez disso, nós podemos reutilizar o nome spaces. Porém se tentarmos utilizar mut nesse caso, como mostrado a seguir, nós teremos um erro em tempo de compilação:

let mut spaces = "   ";
spaces = spaces.len();

O erro diz que nós não podemos mudar o tipo de uma variável:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
error[E0308]: mismatched types
 --> src/main.rs:3:14
  |
3 |     spaces = spaces.len();
  |              ^^^^^^^^^^^^ expected `&str`, found `usize`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` due to previous error

No próximo post vamos aprender sobre outros tipos que as variáveis podem ter.

Referência 📚

Para escrever esse post e os possíveis próximos que virão, estou utilizando o livro online recomendado no site oficial do Rust, que você pode encontrar em doc.rust-lang.org/book/title-page.html.