Variáveis e Mutabilidade

Beginner

This tutorial is from open-source community. Access the source code

Introdução

Bem-vindo a Variáveis e Mutabilidade. Este laboratório faz parte do Livro do Rust. Você pode praticar suas habilidades em Rust no LabEx.

Neste laboratório, exploramos o conceito de mutabilidade em Rust, discutindo como as variáveis são imutáveis por padrão e como torná-las mutáveis usando a palavra-chave mut, e destacando a importância da imutabilidade para segurança e concorrência, ao mesmo tempo em que reconhecemos a utilidade da mutabilidade em certas situações.

Este é um Lab Guiado, que fornece instruções passo a passo para ajudá-lo a aprender e praticar. Siga as instruções cuidadosamente para completar cada etapa e ganhar experiência prática. Dados históricos mostram que este é um laboratório de nível iniciante com uma taxa de conclusão de 87%. Recebeu uma taxa de avaliações positivas de 100% dos estudantes.

Variáveis e Mutabilidade

Como mencionado em "Armazenando Valores com Variáveis", por padrão, as variáveis são imutáveis. Este é um dos muitos incentivos que o Rust oferece para que você escreva seu código de uma forma que aproveite a segurança e a fácil concorrência que o Rust oferece. No entanto, você ainda tem a opção de tornar suas variáveis mutáveis. Vamos explorar como e por que o Rust o incentiva a favorecer a imutabilidade e por que, às vezes, você pode querer optar por não usá-la.

Quando uma variável é imutável, uma vez que um valor é vinculado a um nome, você não pode alterar esse valor. Para ilustrar isso, gere um novo projeto chamado variables em seu diretório project usando cargo new variables.

Em seguida, em seu novo diretório variables, abra src/main.rs e substitua seu código pelo seguinte código, que ainda não compilará:

Nome do arquivo: src/main.rs

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

Salve e execute o programa usando cargo run. Você deve receber uma mensagem de erro sobre um erro de imutabilidade, conforme mostrado nesta saída:

$ 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

Este exemplo mostra como o compilador ajuda você a encontrar erros em seus programas. Os erros do compilador podem ser frustrantes, mas na verdade eles apenas significam que seu programa não está fazendo com segurança o que você deseja que ele faça; eles não significam que você não é um bom programador! Rustaceans experientes ainda recebem erros do compilador.

Você recebeu a mensagem de erro cannot assign twice to immutable variablex``porque tentou atribuir um segundo valor à variável imutávelx.

É importante que obtenhamos erros em tempo de compilação quando tentamos alterar um valor que é designado como imutável, porque essa situação pode levar a bugs. Se uma parte do nosso código opera com a suposição de que um valor nunca mudará e outra parte do nosso código altera esse valor, é possível que a primeira parte do código não faça o que foi projetado para fazer. A causa desse tipo de bug pode ser difícil de rastrear depois, especialmente quando a segunda parte do código altera o valor apenas às vezes. O compilador Rust garante que, quando você afirma que um valor não mudará, ele realmente não mudará, então você não precisa controlá-lo sozinho. Seu código é, portanto, mais fácil de raciocinar.

Mas a mutabilidade pode ser muito útil e pode tornar o código mais conveniente de escrever. Embora as variáveis sejam imutáveis por padrão, você pode torná-las mutáveis adicionando mut na frente do nome da variável, como você fez no Capítulo 2. Adicionar mut também transmite a intenção aos futuros leitores do código, indicando que outras partes do código alterarão o valor desta variável.

Por exemplo, vamos alterar src/main.rs para o seguinte:

Nome do arquivo: src/main.rs

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

Quando executamos o programa agora, obtemos isto:

$ 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

Podemos alterar o valor vinculado a x de 5 para 6 quando mut é usado. Em última análise, decidir se usa ou não a mutabilidade depende de você e do que você acha mais claro naquela situação específica.

Constantes

Como variáveis imutáveis, as constantes são valores que são vinculados a um nome e não podem ser alterados, mas existem algumas diferenças entre constantes e variáveis.

Primeiro, você não pode usar mut com constantes. As constantes não são apenas imutáveis por padrão - elas são sempre imutáveis. Você declara constantes usando a palavra-chave const em vez da palavra-chave let, e o tipo do valor deve ser anotado. Abordaremos tipos e anotações de tipo em "Tipos de Dados", então não se preocupe com os detalhes agora. Apenas saiba que você sempre deve anotar o tipo.

As constantes podem ser declaradas em qualquer escopo, incluindo o escopo global, o que as torna úteis para valores que muitas partes do código precisam conhecer.

A última diferença é que as constantes podem ser definidas apenas para uma expressão constante, não para o resultado de um valor que só pode ser computado em tempo de execução.

Aqui está um exemplo de uma declaração de constante:

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

O nome da constante é THREE_HOURS_IN_SECONDS e seu valor é definido como o resultado da multiplicação de 60 (o número de segundos em um minuto) por 60 (o número de minutos em uma hora) por 3 (o número de horas que queremos contar neste programa). A convenção de nomenclatura do Rust para constantes é usar tudo em maiúsculas com sublinhados entre as palavras. O compilador é capaz de avaliar um conjunto limitado de operações em tempo de compilação, o que nos permite escolher escrever este valor de uma forma que seja mais fácil de entender e verificar, em vez de definir esta constante para o valor 10.800. Consulte a seção de avaliação de constantes da Referência do Rust em https://doc.rust-lang.org/reference/const_eval.html para obter mais informações sobre quais operações podem ser usadas ao declarar constantes.

As constantes são válidas durante todo o tempo de execução de um programa, dentro do escopo em que foram declaradas. Essa propriedade torna as constantes úteis para valores no seu domínio de aplicação que várias partes do programa podem precisar conhecer, como o número máximo de pontos que qualquer jogador de um jogo pode ganhar ou a velocidade da luz.

Nomear valores codificados usados em todo o seu programa como constantes é útil para transmitir o significado desse valor aos futuros mantenedores do código. Também ajuda ter apenas um lugar no seu código que você precisaria alterar se o valor codificado precisasse ser atualizado no futuro.

Shadowing (Sombreamento)

Como você viu no tutorial do jogo de adivinhação no Capítulo 2, você pode declarar uma nova variável com o mesmo nome de uma variável anterior. Os Rustaceans dizem que a primeira variável é shadowed (sombreada) pela segunda, o que significa que a segunda variável é o que o compilador verá quando você usar o nome da variável. Na prática, a segunda variável ofusca a primeira, assumindo qualquer uso do nome da variável até que ela própria seja sombreada ou o escopo termine. Podemos sombrear uma variável usando o nome da mesma variável e repetindo o uso da palavra-chave let, da seguinte forma:

Nome do 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}");
}

Este programa primeiro vincula x a um valor de 5. Em seguida, ele cria uma nova variável x repetindo let x =, pegando o valor original e adicionando 1, de modo que o valor de x seja então 6. Em seguida, dentro de um escopo interno criado com as chaves, a terceira instrução let também sombreia x e cria uma nova variável, multiplicando o valor anterior por 2 para dar a x um valor de 12. Quando esse escopo termina, o sombreamento interno termina e x volta a ser 6. Quando executamos este programa, ele exibirá o seguinte:

$ 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

O sombreamento é diferente de marcar uma variável como mut porque receberemos um erro em tempo de compilação se tentarmos, acidentalmente, reatribuir a esta variável sem usar a palavra-chave let. Usando let, podemos realizar algumas transformações em um valor, mas fazer com que a variável seja imutável após a conclusão dessas transformações.

A outra diferença entre mut e sombreamento é que, como estamos efetivamente criando uma nova variável quando usamos a palavra-chave let novamente, podemos alterar o tipo do valor, mas reutilizar o mesmo nome. Por exemplo, digamos que nosso programa peça a um usuário para mostrar quantos espaços ele deseja entre algum texto, inserindo caracteres de espaço, e então queremos armazenar essa entrada como um número:

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

A primeira variável spaces é do tipo string e a segunda variável spaces é do tipo número. O sombreamento, portanto, nos poupa de ter que inventar nomes diferentes, como spaces_str e spaces_num; em vez disso, podemos reutilizar o nome mais simples spaces. No entanto, se tentarmos usar mut para isso, como mostrado aqui, receberemos um erro em tempo de compilação:

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

O erro diz que não podemos mutar 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
  |
2 |     let mut spaces = "   ";
  |                      ----- expected due to this value
3 |     spaces = spaces.len();
  |              ^^^^^^^^^^^^ expected `&str`, found `usize`

Agora que exploramos como as variáveis funcionam, vamos analisar mais tipos de dados que elas podem ter.

Resumo

Parabéns! Você concluiu o laboratório de Variáveis e Mutabilidade. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.