Variáveis, mutabilidade e shadowing
1. Introdução às Variáveis em Rust
Em Rust, toda variável precisa ser declarada antes de ser usada. A forma mais básica de declaração utiliza a palavra-chave let, que permite ao compilador inferir o tipo da variável automaticamente na maioria dos casos:
let idade = 25; // i32 inferido
let nome = "Ana"; // &str inferido
let altura = 1.75; // f64 inferido
A característica mais marcante das variáveis em Rust é que elas são imutáveis por padrão. Isso significa que, uma vez atribuído um valor, você não pode alterá-lo:
let x = 5;
x = 6; // Erro! Não é possível atribuir duas vezes a uma variável imutável
Essa imutabilidade padrão não é um capricho — ela traz benefícios reais de segurança e previsibilidade. Em programas concorrentes, variáveis imutáveis eliminam inteiramente a possibilidade de data races. Em código sequencial, torna mais fácil raciocinar sobre o fluxo do programa, pois você sabe que um valor não será modificado inesperadamente.
Boas práticas: prefira sempre variáveis imutáveis. Use mutabilidade apenas quando houver uma razão clara e necessária.
2. Mutabilidade com let mut
Quando você precisa que uma variável mude de valor ao longo do tempo, use let mut:
let mut contador = 0;
contador += 1; // Agora contador vale 1
contador += 1; // Agora contador vale 2
Situações comuns que exigem mutabilidade incluem:
- Loops e acumuladores: somar valores, contar iterações
- Estado mutante: variáveis que representam estado de uma aplicação (posição em um jogo, saldo bancário)
- Buffers e coleções: vetores que recebem novos elementos
let mut soma = 0;
for i in 1..=10 {
soma += i;
}
println!("Soma de 1 a 10: {}", soma); // 55
É importante distinguir mutabilidade da variável de mutabilidade do dado. A variável mut indica que podemos alterar a referência ou o valor que ela armazena. Dados em si (como structs) podem ter campos mutáveis independentemente.
struct Ponto {
x: i32,
y: i32,
}
let mut p = Ponto { x: 10, y: 20 };
p.x = 30; // Permitido porque a variável p é mutável
3. Constantes com const
Constantes são declaradas com const e exigem tipo explícito:
const PI: f64 = 3.14159;
const MAX_USUARIOS: u32 = 100_000;
Diferenças cruciais entre const e let imutável:
| Característica | const |
let (imutável) |
|---|---|---|
| Tipo explícito | Obrigatório | Opcional (inferido) |
| Momento da avaliação | Compilação | Runtime |
| Escopo | Global ou local | Local |
| Expressões permitidas | Apenas constantes | Qualquer expressão |
const TAXA: f64 = 0.15;
fn calcula_imposto(valor: f64) -> f64 {
valor * TAXA // TAXA é conhecida em tempo de compilação
}
Use constantes para valores mágicos, limites máximos, taxas e configurações que não mudam. Isso melhora a legibilidade e facilita manutenção.
4. Shadowing: Reutilizando Nomes
Shadowing (sombreamento) permite redeclarar uma variável com o mesmo nome, criando uma nova variável que "esconde" a anterior:
let x = 5;
let x = x + 1; // Novo x vale 6, o antigo não existe mais
let x = x * 2; // Novo x vale 12
println!("x = {}", x); // 12
Uma característica poderosa do shadowing é que você pode mudar o tipo da variável:
let espaco = " "; // &str
let espaco = espaco.len(); // usize (número de espaços)
println!("Tamanho: {}", espaco); // 3
Isso é útil em transformações em etapas, onde uma variável passa por diferentes representações:
let entrada = "42";
let entrada: i32 = entrada.parse().expect("Não é número");
let entrada = entrada + 10;
println!("Resultado: {}", entrada); // 52
5. Shadowing vs. Mutabilidade
Embora ambos permitam alterar o valor associado a um nome, shadowing e mutabilidade são conceitos fundamentalmente diferentes:
Mutabilidade (let mut) altera a mesma variável. A variável mantém sua identidade e tipo.
Shadowing (let) cria uma nova variável. A antiga é descartada, e a nova pode ter tipo diferente.
// Mutabilidade
let mut y = 10;
y = y + 5; // y é a mesma variável, ainda i32
// Shadowing
let z = 10;
let z = z + 5; // z é uma nova variável
Quando preferir shadowing:
- Transformações em etapas com mudança de tipo
- Operações temporárias que não precisam de escopo adicional
- Melhorar clareza ao reutilizar um nome significativo
// Shadowing elegante para transformações
let nome_completo = "Maria Silva";
let nome_completo = nome_completo.trim();
let nome_completo = nome_completo.to_lowercase();
6. Escopo e Ciclo de Vida das Variáveis
Variáveis em Rust existem dentro de blocos {}. Ao sair do bloco, a variável é destruída:
let a = 10;
{
let b = 20;
println!("Dentro: a={}, b={}", a, b); // a=10, b=20
}
// println!("{}", b); // Erro! b não existe mais
Shadowing dentro de escopos internos é uma técnica poderosa:
let x = 5;
{
let x = 10; // Sombra a variável externa
println!("Interno: {}", x); // 10
}
println!("Externo: {}", x); // 5 (a original permanece)
Isso permite limitar o efeito de transformações temporárias sem poluir o escopo externo.
7. Exemplos Práticos e Boas Práticas
Exemplo 1: Calcular média com shadowing
fn main() {
let notas = vec![8.5, 7.0, 9.2, 6.8];
// Shadowing para transformar a soma em média
let media = notas.iter().sum::<f64>();
let media = media / notas.len() as f64;
println!("Média: {:.2}", media);
}
Exemplo 2: Processamento de entrada do usuário
use std::io;
fn main() {
println!("Digite sua idade:");
let mut entrada = String::new();
io::stdin().read_line(&mut entrada).expect("Erro de leitura");
let entrada = entrada.trim(); // Remove quebra de linha
let idade: u32 = entrada.parse().expect("Idade inválida");
if idade >= 18 {
println!("Maior de idade");
} else {
println!("Menor de idade");
}
}
Dicas finais:
- Evite shadowing excessivo que dificulte a leitura
- Prefira nomes descritivos diferentes para conceitos diferentes
- Use shadowing para transformações claras em etapas
- Prefira imutabilidade sempre que possível
- Use
constpara valores que são realmente constantes em tempo de compilação
Dominar variáveis, mutabilidade e shadowing é essencial para escrever código Rust idiomático, seguro e eficiente. Esses conceitos formam a base para entender ownership, empréstimos e lifetimes — os pilares da segurança de memória em Rust.
Referências
- The Rust Programming Language - Variables and Mutability — Capítulo oficial do livro que cobre declaração de variáveis, mutabilidade e constantes.
- Rust by Example - Variable Bindings — Exemplos práticos de bindings, mutabilidade e escopo.
- Rust Reference - Variables — Documentação de referência detalhada sobre variáveis e shadowing.
- Rust Lang Blog - Why Rust's Ownership Model Matters — Artigo discutindo como imutabilidade e ownership habilitam concorrência segura.
- Rust Documentation -
constandstatic— Documentação oficial sobre constantes e itens estáticos em Rust. - Rust Design Patterns - Immutability — Padrões de design relacionados ao uso de imutabilidade em Rust.