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


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:

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

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

RUST
let x = 5;       // Immutable Variable
// x = 10;       // ❌ Compilation Error: cannot assign twice to immutable variable

(2) let mut: variáveis mutáveis

RUST
let mut y = 5;   // Mutable Variable
y = 10;          // ✅ Can be reassigned

(3) const: Constante em tempo de compilação

RUST
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.

100%
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

RUST
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

RUST
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

RUST
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
// ============================================
// 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:

TEXT 📖 Somente leitura
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 ⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
small: 255, big: 9223372036854775807, precise: 3.1415927410, flag: true

Observe que f32 tem uma precisão de apenas cerca de 7 dígitos significativos, e 3.141592653589793 é truncado para 3.1415927410.


▶ Exemplo 3: Ocultação de variáveis (Dificuldade ⭐⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
x value: 12
name length: 4

Shadowing ≠ mutabilidade. O shadowing cria uma nova variável (nova memória), enquanto mut modifica o mesmo bloco de memória. O shadowing pode alterar o tipo de uma variável dentro do mesmo escopo, mas mut não.


▶ Exemplo 4: Exercício abrangente — Widget de gerenciamento de estoque (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
=== 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ável let, da variável mutável let mut e do ocultamento de variáveis (em que o tipo ocultado muda de f64 para String), 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 ⭐⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
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_* e wrapping_* 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: ⭐⭐⭐)

RUST
// ============================================
// 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:

TEXT 📖 Somente leitura
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 &strResult<f64>f64i32String — 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 é i32 em vez de i64? 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. Use i64 explicitamente 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), enquanto mut modifica a mesma variável (sem alterar seu tipo). O shadowing é adequado para cenários de “conversão de tipos” (por exemplo, string → comprimento), enquanto mut é adequado para cenários de acumulação.

P: Um char ocupa 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 de char. 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 tipo char é usado principalmente para lidar com caracteres Unicode individuais.

P: Qual é a diferença entre const e let? R: const é incorporado em tempo de compilação, enquanto let é alocado em tempo de execução. O valor de const é 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


📝 Exercícios

  1. Dificuldade ⭐: Declare uma variável do tipo u16 e atribua a ela o valor 65535; em seguida, tente alterá-la para 65536 e observe a mensagem de erro do compilador.
  2. Dificuldade ⭐⭐: Escreva um programa que use let mut para declarar uma variável que acumule a soma dos números de 1 a 5 e exiba o resultado.
  3. Dificuldade ⭐⭐⭐: Tente declarar uma constante const fora da função fn main() e usá-la dentro da função. Em seguida, tente declarar uma variável let fora da função e observe o erro do compilador — isso ajudará você a entender o escopo global de const.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%