Rust: Variáveis e tipos de dados em Rust
Última atualização: 2026-08-26
No Rust, as variáveis são imutáveis por padrão — isso não é uma limitação, mas uma garantia de segurança.
Os sistemas de variáveis e de tipos do Rust são a base de seu compromisso com a segurança de memória. Compreender a imutabilidade, a inferência de tipos e os tipos escalares é o primeiro passo para dominar o Rust.
1. O que você vai aprender
- Declare uma variável usando
letpara entender a imutabilidade - Criar uma variável mutável usando
let mut - Use
constpara definir uma constante em tempo de compilação - Os quatro tipos escalares do Rust: inteiros, números de ponto flutuante, booleanos e caracteres
- Inferência de tipos e anotação explícita de tipos
- Mecanismo de sombreamento variável
2. Uma história real de uma caixa
(1) O desafio: corrigir erros numéricos no Excel
Lisa é caixa em um pequeno supermercado. Todos os dias, depois do trabalho, ela usa o Excel para contabilizar as vendas do dia:
- O preço muda constantemente (devido a promoções, descontos e preços para membros), por isso ela precisa atualizar manualmente as células.
- Certa vez, ela acidentalmente substituiu uma linha de fórmulas por números, e toda a planilha ficou inutilizada.
- Levou 3 horas para fazer a reconciliação das contas novamente
Depois de aprender Rust, ela comentou: “Se as células do Excel fossem indedituáveis por padrão, eu não precisaria me preocupar em sobrescrever fórmulas.”
(2) O modelo de variáveis do Rust
fn main() {
let total_sales: f64 = 1280.50; // Immutable by default -- Today's Sales
// total_sales = 1500.00; // ❌ Compilation Error: You cannot modify immutable variables!
let mut daily_changes = 0.0; // Use mut to declare a mutable variable
daily_changes = 150.0; // ✅ Can be modified
daily_changes = daily_changes + 200.0;
const TAX_RATE: f64 = 0.08; // Compile-time constant, Global Fixed
println!("Tax Rate: {}, Today's Accounts Receivable: {}", TAX_RATE, total_sales * (1.0 + TAX_RATE));
}
A filosofia do Rust é: Se puder permanecer inalterado, não mude. A imutabilidade por padrão facilita a compreensão do código e reduz os erros causados por modificações não intencionais.
3. Declaração de variáveis
(1) let: imutável por padrão
let x = 5; // Immutable Variable
// x = 10; // ❌ Compilation Error: cannot assign twice to immutable variable
(2) let mut: variáveis mutáveis
let mut y = 5; // Mutable Variable
y = 10; // ✅ Can be reassigned
(3) const: Constante em tempo de compilação
const MAX_POINTS: u32 = 100_000; // The type must be specified.
const PI: f64 = 3.1415926535;
| Características | let |
let mut |
const |
|---|---|---|---|
| Variabilidade | Imutável | Mutável | Imutável |
| Existe em tempo de execução | Sim | Sim | Não (incorporado em tempo de compilação) |
| Anotação de tipo | Opcional | Opcional | Obrigatório |
| Âmbito global | Não | Não | Sim |
| Avaliação da expressão | Tempo de execução | Tempo de execução | Constante de tempo de compilação |
4. Tipos escalares
O Rust possui quatro tipos escalares básicos: inteiros, números de ponto flutuante, booleanos e caracteres.
graph TB
A[Scalar Types] --> B[Integer: i8/u8/i16/u16/i32/u32/i64/u64/i128/u128]
A --> C[Floating-point: f32/f64]
A --> D[Boolean: bool]
A --> E[Character: char]
(1) Tipos inteiros
| Comprimento | Com sinal | Sem sinal | Intervalo |
|---|---|---|---|
| 8 bits | i8 |
u8 |
-128 a 127 / 0 a 255 |
| 16 bits | i16 |
u16 |
-32768 ~ 32767 / 0 ~ 65535 |
| 32 bits | i32 |
u32 |
±2,1 bilhões / 0 ~ 4,2 bilhões |
| 64 bits | i64 |
u64 |
±9,2 × 10¹⁸ / 0 ~ 1,8 × 10¹⁹ |
| 128 bits | i128 |
u128 |
Alcance máximo |
| arquitetura | isize |
usize |
Igual à largura de bits do sistema (sistema de 64 bits = i64/u64) |
O tipo inteiro padrão é
i32— ele oferece o melhor desempenho e é suficiente para a maioria das necessidades.
(2) Tipos de ponto flutuante
let a: f32 = 3.14; // 32-bit precision floating-point (Single Precision)
let b: f64 = 3.141592653589793; // 64-bit precision floating-point (Double Precision, Default Type)
O tipo padrão de ponto flutuante é
f64— em CPUs modernas, f64 e f32 têm praticamente a mesma velocidade, mas f64 oferece maior precisão.
(3) Tipo booleano
let is_ok: bool = true;
let is_not = false;
Os valores booleanos são comumente usados em instruções condicionais, como as expressões condicionais if e while.
(4) Tipos de caracteres
let c: char = 'A'; // ASCII Character
let emoji: char = '🦀'; // Unicode Character(4 Byte)
let han: char = 'Rust'; // Unicode character
No Rust,
charé um valor escalar Unicode que ocupa 4 bytes. Não é ASCII! Isso proporciona ao Rust suporte nativo para internacionalização.
| Tipo | Tamanho | Faixa de valores | Usos típicos |
|---|---|---|---|
bool |
1 byte | true / false |
Verificações condicionais, sinalizadores |
char |
4 bytes | Valor escalar Unicode | Processamento de um único caractere |
f32 |
4 bytes | ±3,4×10³⁸ (precisão de aproximadamente 7 dígitos) | Computação gráfica, shaders de GPU |
f64 |
8 bytes | ±1,8×10³⁰⁸ (precisão de aproximadamente 15 dígitos) | Computação científica (ponto flutuante padrão) |
i32 |
4 bytes | ±2,1 bilhões | Inteiro de uso geral (padrão) |
u8 |
1 byte | 0–255 | Dados em bytes, valores RGB |
usize |
arch | Igual à largura em bits do sistema | Índices de matriz, tamanho do contêiner |
5. Inferência de tipos e anotação
▶ Exemplo 1: Inferência de tipos (Dificuldade ⭐)
// ============================================
// Rust compiler infers types based on assignments
// ============================================
fn main() {
let x = 42; // Inferred as i32 (Default integer type)
let y = 3.14; // Inferred as f64 (Default floating-point type)
let z = true; // Inferred as bool
let c = 'R'; // Inferred as char
// Can use :type to view (Example in This Section, Not a standard method)
println!("The value of x is: {}", x);
println!("The value of y is: {}", y);
}
Resultado:
x The value is: 42
y The value is: 3.14
O compilador infere automaticamente os tipos com base no contexto, de modo que os desenvolvedores não precisam especificar anotações de tipo em todos os lugares.
▶ Exemplo 2: Anotação explícita de tipo (Dificuldade ⭐)
// ============================================
// Explicitly Specify the Type -- When a specific size or precision is required
// ============================================
fn main() {
let small: u8 = 255; // u8 Maximum value
let big: i64 = 9_223_372_036_854_775_807; // An underscore can be used as a digit separator
let precise: f32 = 3.141592653589793; // f32 May result in loss of precision
let flag: bool = 5 > 3; // The result of a Boolean expression
println!("small: {}, big: {}, precise: {:.10}, flag: {}", small, big, precise, flag);
}
Resultado:
small: 255, big: 9223372036854775807, precise: 3.1415927410, flag: true
Observe que
f32tem uma precisão de apenas cerca de 7 dígitos significativos, e3.141592653589793é truncado para3.1415927410.
▶ Exemplo 3: Ocultação de variáveis (Dificuldade ⭐⭐)
// ============================================
// Shadowing: Overwrite a previous variable with the same name
// ============================================
fn main() {
let x = 5; // First x
let x = x + 1; // Shadowing: Second x, value becomes 6
let x = x * 2; // Shadowing: Third x, value becomes 12
// Shadowing allows for a change in type!
let name = "Rust"; // &str Type
let name = name.len(); // became usize Type!
println!("x value: {}", x);
println!("name length: {}", name);
}
Resultado:
x value: 12
name length: 4
Shadowing ≠ mutabilidade. O shadowing cria uma nova variável (nova memória), enquanto
mutmodifica o mesmo bloco de memória. O shadowing pode alterar o tipo de uma variável dentro do mesmo escopo, masmutnão.
▶ Exemplo 4: Exercício abrangente — Widget de gerenciamento de estoque (Dificuldade ⭐⭐⭐)
// ============================================
// Comprehensive Example: Using Variables, Shadowing and Constants in Inventory Management
// Demo let/mut/const/shadowing Practical Applications
// ============================================
const MAX_STOCK: u32 = 10_000;
const DISCOUNT_THRESHOLD: u32 = 500;
fn main() {
let item_name = "Wireless Mouse";
let price: f64 = 49.99;
let mut stock: u32 = 2500;
println!("=== Inventory Management System ===");
println!("Products: {}", item_name);
println!("Unit Price: {:.2} yuan", price);
println!("Inventory: {} items", stock);
stock = stock - 300;
println!("\n--- Shipped Today 300 items ---");
println!("Remaining Inventory: {} items", stock);
let price = price * 0.85;
println!("\n--- Member Discount 15% off ---");
println!("Member Price: {:.2} yuan", price);
let price = format!("{:.2} yuan", price);
println!("Price Tag: {}", price);
let total_value = (stock as f64) * 49.99;
println!("\nTotal Inventory Value: {:.2} yuan", total_value);
if stock > DISCOUNT_THRESHOLD {
println!("Ample inventory (> {}), No need to restock", DISCOUNT_THRESHOLD);
} else {
let needed = MAX_STOCK - stock;
println!("Out of Stock! Suggested Reorder {} items", needed);
}
println!("Maximum Capacity: {} items", MAX_STOCK);
}
Resultado:
=== Inventory Management System ===
Products: Wireless Mouse
Unit Price: 49.99 yuan
Inventory: 2500 items
--- Shipped Today 300 items ---
Remaining Inventory: 2200 items
--- Member Discount 15% off ---
Member Price: 42.49 yuan
Price Tag: 42.49 yuan
Total Inventory Value: 109978.00 yuan
Ample inventory (> 500), No need to restock
Maximum Capacity: 10000 items
Este exemplo combina o uso da constante
const, da variável imutávellet, da variável mutávellet mute do ocultamento de variáveis (em que o tipo ocultado muda def64paraString), demonstrando como esses diferentes métodos de declaração de variáveis funcionam em conjunto em um cenário do mundo real.
▶ Exemplo 5: Estouro de inteiros e aritmética segura (Dificuldade ⭐⭐)
// ============================================
// Integer Overflow Behavior: debug mode panic vs release mode wrapping
// ============================================
fn main() {
let max_u8: u8 = 255;
println!("u8 Maximum value: {}", max_u8);
// Safe Computation Method (No panic, Returns overflow result)
let (result, did_overflow) = max_u8.overflowing_add(1);
println!("255 + 1 (overflowing_add): {}, Did overflow?: {}", result, did_overflow);
let checked = max_u8.checked_add(1);
println!("255 + 1 (checked_add): {:?}", checked);
let saturating = max_u8.saturating_add(100);
println!("255 + 100 (saturating_add): {}", saturating);
let wrapping: u8 = max_u8.wrapping_add(1);
println!("255 + 1 (wrapping_add): {}", wrapping);
let neg_i8: i8 = -128;
let (abs_result, overflow) = neg_i8.overflowing_abs();
println!("i8 absolute value of minimum: {}, Did overflow?: {}", abs_result, overflow);
}
Resultado:
u8 Maximum value: 255
255 + 1 (overflowing_add): 0, Did overflow?: true
255 + 1 (checked_add): None
255 + 100 (saturating_add): 255
255 + 1 (wrapping_add): 0
i8 absolute value of minimum: -128, Did overflow?: true
No Rust, o estouro de inteiros causa um panic no modo de depuração, mas resulta em um retorno ao início no modo de compilação. Recomenda-se usar os métodos
checked_*,saturating_*ewrapping_*para controlar explicitamente o comportamento do estouro.
▶ Exemplo 6: Aplicação prática do shadowing de variáveis e da conversão de tipos (Dificuldade: ⭐⭐⭐)
// ============================================
// Practical Examples of Variable Shadowing: String Parsing Chain
// ============================================
fn main() {
let input = "42.5";
println!("Original Input: {} (Type: &str)", input);
let input = input.parse::<f64>();
println!("Analysis Results: {:?}", input);
let input = match input {
Ok(value) => value,
Err(_) => 0.0,
};
println!("After unpacking: {} (Type: f64)", input);
let input = input as i32;
println!("Round to an integer: {} (Type: i32, Truncation of Decimals)", input);
let input = input * 2;
println!("After doubling: {} (Type: i32)", input);
let input = format!("Result: {}", input);
println!("Final Output: {} (Type: String)", input);
let alice_score = "95";
let alice_score = alice_score.parse::<u32>().unwrap_or(0);
let alice_grade = if alice_score >= 90 { "A" } else { "B" };
println!("Alice: Score={}, Level={}", alice_score, alice_grade);
}
Resultado:
Original Input: 42.5 (Type: &str)
Analysis Results: Ok(42.5)
After unpacking: 42.5 (Type: f64)
Round to an integer: 42 (Type: i32, Truncation of Decimals)
After doubling: 84 (Type: i32)
Final Output: Result: 84 (Type: String)
Alice: Score=95, Level=A
O sombreamento de variáveis permite uma conversão gradual de tipos de dados dentro do mesmo escopo — de
&str→Result<f64>→f64→i32→String— utilizando o mesmo nome de variável em cada etapa, resultando em um código claro e sem redundâncias.
❓ Perguntas Frequentes
P: Qual é a diferença entre i32 e u32? R: Os valores que começam com “i” são assinados (podem ser positivos ou negativos), enquanto aqueles que começam com “u” são sem sinal (só podem ser não negativos). Use i32 para inteiros de uso geral e u32 quando tiver certeza de que o valor nunca será negativo (como o comprimento de uma matriz).
P: Por que o padrão no Rust é
i32em vez dei64? R: Por questões de desempenho.i32é o mais rápido na maioria das arquiteturas e é suficiente para representar 99% dos intervalos numéricos do dia a dia. Usei64explicitamente apenas quando for necessário um intervalo maior.
P: Qual é a diferença entre shadowing e
let mut? R: O shadowing cria uma nova variável (com um tipo diferente), enquantomutmodifica a mesma variável (sem alterar seu tipo). O shadowing é adequado para cenários de “conversão de tipos” (por exemplo, string → comprimento), enquantomuté adequado para cenários de acumulação.
P: Um
charocupa 4 bytes; então, as strings não são um desperdício de espaço? R: As strings do Rust não são matrizes dechar. As strings são armazenadas internamente usando a codificação UTF-8, com cada caractere ocupando de 1 a 4 bytes — não são 4 bytes fixos. O tipocharé usado principalmente para lidar com caracteres Unicode individuais.
P: Qual é a diferença entre
constelet? R:consté incorporado em tempo de compilação, enquantoleté alocado em tempo de execução. O valor deconsté substituído em todas as ocorrências em tempo de compilação, tornando-o adequado para constantes matemáticas verdadeiras e substituições de números mágicos.
📖 Resumo
- As variáveis do Rust são imutáveis por padrão; use
mutpara declarar variáveis mutáveis constAs constantes de tempo de compilação devem ter anotação de tipo e seguir a convenção de nomenclatura em letras maiúsculas- Os tipos inteiros são classificados como assinados (i) e não assinados (u); por padrão
i32 - O formato padrão de ponto flutuante é
f64;charocupa 4 bytes e é compatível com Unicode. - O compilador do Rust possui uma poderosa inferência de tipos, mas é possível especificar explicitamente os tipos quando necessário.
- O sombreamento de variáveis permite que uma nova variável com o mesmo nome substitua uma variável anterior; ela pode até mesmo alterar o tipo.
📝 Exercícios
- Dificuldade ⭐: Declare uma variável do tipo
u16e atribua a ela o valor 65535; em seguida, tente alterá-la para 65536 e observe a mensagem de erro do compilador. - Dificuldade ⭐⭐: Escreva um programa que use
let mutpara declarar uma variável que acumule a soma dos números de 1 a 5 e exiba o resultado. - Dificuldade ⭐⭐⭐: Tente declarar uma constante
constfora da funçãofn main()e usá-la dentro da função. Em seguida, tente declarar uma variávelletfora da função e observe o erro do compilador — isso ajudará você a entender o escopo global deconst.