Tipos escalares: inteiros, floats, bool e char
1. Introdução aos tipos escalares em Rust
Tipos escalares representam um único valor. Em Rust, eles são a base sobre a qual tipos mais complexos são construídos. Diferentemente de tipos compostos (arrays, tuplas, structs), que agrupam múltiplos valores, os escalares armazenam exatamente um valor por vez.
Rust possui quatro tipos escalares fundamentais:
- Inteiros: números inteiros com ou sem sinal
- Floats: números de ponto flutuante
- Bool: valores lógicos verdadeiro/falso
- Char: caracteres Unicode
Cada tipo tem características específicas de tamanho, faixa de valores e comportamento, que veremos em detalhes.
2. Inteiros: representação de números inteiros
Rust oferece uma rica variedade de tipos inteiros, divididos em duas categorias:
Com sinal (i = signed): podem ser positivos ou negativos
- i8, i16, i32, i64, i128, isize
Sem sinal (u = unsigned): apenas não-negativos
- u8, u16, u32, u64, u128, usize
O número no nome indica a quantidade de bits. isize e usize dependem da arquitetura da máquina (32 ou 64 bits).
fn main() {
let pequeno: i8 = -128;
let grande: u64 = 18_446_744_073_709_551_615;
let arquitetura: usize = 42; // 64 bits em sistemas modernos
}
Faixas de valores:
- i8: -128 a 127
- u8: 0 a 255
- i32: -2.147.483.648 a 2.147.483.647
- u64: 0 a 18.446.744.073.709.551.615
Literais inteiros podem ser escritos de várias formas:
fn main() {
let decimal = 98_222; // separador visual _
let hex = 0xff; // 255 em decimal
let octal = 0o77; // 63 em decimal
let binario = 0b1111_0000; // 240 em decimal
let byte = b'A'; // 65 (apenas para u8)
}
3. Comportamentos especiais dos inteiros
O tratamento de overflow é um dos aspectos mais distintos de Rust:
fn main() {
// Em debug: panic!
// Em release: wrapping (volta ao início)
let mut x: u8 = 255;
// x = x + 1; // panic em debug, vira 0 em release
// Alternativas seguras:
let (resultado, houve_overflow) = 255u8.overflowing_add(1);
println!("Overflow: {}, resultado: {}", houve_overflow, resultado);
let satura = 255u8.saturating_add(1);
println!("Saturado: {}", satura); // 255
let checked = 255u8.checked_add(1);
match checked {
Some(valor) => println!("OK: {}", valor),
None => println!("Overflow detectado!"),
}
let wrapped = 255u8.wrapping_add(1);
println!("Wrapping: {}", wrapped); // 0
}
Conversões entre tipos inteiros:
fn main() {
// Conversão com 'as' (pode truncar)
let grande: u32 = 300;
let pequeno: u8 = grande as u8; // 44 (truncado)
println!("Truncado: {}", pequeno);
// Conversão segura com TryFrom
let resultado = u8::try_from(300u32);
match resultado {
Ok(valor) => println!("Conversão OK: {}", valor),
Err(e) => println!("Erro: {}", e),
}
// From funciona apenas quando não há perda
let valor: u32 = u32::from(255u8); // sempre OK
}
4. Floats: números de ponto flutuante
Rust oferece dois tipos de ponto flutuante: f32 (precisão simples, 32 bits) e f64 (precisão dupla, 64 bits). O padrão é f64 por oferecer maior precisão.
fn main() {
let precisao_simples: f32 = 3.14;
let precisao_dupla = 3.141592653589793; // f64 por padrão
// Notação científica
let grande = 1.5e10; // 15.000.000.000
let pequeno = 2.5e-4; // 0.00025
println!("f32: {}", precisao_simples);
println!("f64: {}", precisao_dupla);
println!("Científico: {} e {}", grande, pequeno);
}
Operações aritméticas e funções matemáticas:
fn main() {
let a = 10.5;
let b = 3.2;
// Operações básicas
println!("Soma: {}", a + b);
println!("Subtração: {}", a - b);
println!("Multiplicação: {}", a * b);
println!("Divisão: {}", a / b);
println!("Resto: {}", a % b);
// Métodos matemáticos
println!("Raiz quadrada: {}", (25.0_f64).sqrt());
println!("Potência: {}", (2.0_f64).powi(3)); // 8.0
println!("Valor absoluto: {}", (-3.5_f64).abs());
println!("Arredondamento: {}", (3.7_f64).round());
// Constantes especiais
println!("PI: {}", std::f64::consts::PI);
println!("Infinito: {}", f64::INFINITY);
println!("NaN: {}", f64::NAN);
}
5. Bool: o tipo lógico
O tipo bool representa valores lógicos com apenas dois estados possíveis: true e false.
fn main() {
let verdadeiro: bool = true;
let falso: bool = false;
// Operadores booleanos
let e_logico = verdadeiro && falso; // false
let ou_logico = verdadeiro || falso; // true
let negacao = !verdadeiro; // false
println!("AND: {}", e_logico);
println!("OR: {}", ou_logico);
println!("NOT: {}", negacao);
// Uso em expressões condicionais
let idade = 18;
let pode_votar = idade >= 16;
if pode_votar {
println!("Pode votar!");
} else {
println!("Não pode votar");
}
// Comparações retornam bool
let x = 10;
let y = 20;
println!("x == y: {}", x == y); // false
println!("x < y: {}", x < y); // true
println!("x != y: {}", x != y); // true
}
6. Char: o tipo caractere Unicode
Diferente de C/C++, onde char tem 1 byte, em Rust char tem 4 bytes e pode representar qualquer caractere Unicode.
fn main() {
let letra: char = 'a';
let emoji = '😊';
let escape = '\n'; // nova linha
let unicode = '\u{1F600}'; // 😀 em hexadecimal
println!("Letra: {}", letra);
println!("Emoji: {}", emoji);
println!("Unicode: {}", unicode);
// Escapes especiais
let tab = '\t';
let barra_invertida = '\\';
let aspas_simples = '\'';
let aspas_duplas = '"'; // não precisa de escape em char
println!("Tab{}aqui", tab);
println!("Barra: {}", barra_invertida);
println!("Aspas: {}", aspas_simples);
// Tamanho em bytes (4 bytes cada)
println!("Tamanho de 'a': {} bytes", std::mem::size_of_val(&letra));
println!("Tamanho de '😊': {} bytes", std::mem::size_of_val(&emoji));
}
7. Comparações e operações entre tipos escalares
Rust é fortemente tipada e não permite operações entre tipos diferentes sem conversão explícita:
fn main() {
// Erro comum: misturar tipos
let inteiro: i32 = 10;
let flutuante: f64 = 3.14;
// let soma = inteiro + flutuante; // ERRO! Tipos diferentes
// Correto: conversão explícita
let soma = inteiro as f64 + flutuante;
println!("Soma: {}", soma);
// Outro erro comum
let a: u8 = 200;
let b: u16 = 100;
// let c = a + b; // ERRO! u8 + u16
// Soluções:
let c1 = a as u16 + b;
let c2 = a + b as u8; // cuidado com overflow!
println!("c1: {}, c2: {}", c1, c2);
// Comparações entre tipos diferentes
let x: i32 = 5;
let y: f64 = 5.0;
// println!("{}", x == y); // ERRO! Não pode comparar
println!("{}", (x as f64) == y); // true
}
Boas práticas para conversões:
- Use as apenas quando tem certeza que não há perda
- Prefira From/TryFrom para conversões seguras
- Evite misturar inteiros com sinal e sem sinal em operações aritméticas
8. Resumo e dicas de uso prático
Quando usar cada tipo escalar:
| Tipo | Uso principal |
|---|---|
i32 |
Padrão para inteiros, melhor performance em CPUs modernas |
u8 |
Dados binários, bytes, valores pequenos (0-255) |
u64/i64 |
Valores muito grandes, timestamps |
usize |
Indexação de arrays, tamanhos de coleções |
f64 |
Padrão para floats, cálculos científicos |
f32 |
Gráficos 3D, quando memória é crítica |
bool |
Flags, condições lógicas |
char |
Caracteres Unicode individuais |
Escolha inteligente de tipos:
fn main() {
// Para a maioria dos casos, i32 é suficiente
let contador: i32 = 42;
// Para loops com coleções, use usize
let indices: Vec<usize> = vec![0, 1, 2, 3];
// Para economizar memória em grandes volumes
let pixels: Vec<u8> = vec![255, 128, 64]; // cores RGB
// Para cálculos financeiros, considere bibliotecas especializadas
// (f64 tem problemas de precisão para dinheiro)
}
Referência rápida para literais e conversões:
fn main() {
// Literais com sufixo de tipo
let x = 42i32; // i32
let y = 3.14f32; // f32
let z = 100u8; // u8
// Conversões comuns
let de_int_para_float = 42i32 as f64; // 42.0
let de_float_para_int = 3.99_f64 as i32; // 3 (trunca)
let de_bool_para_int = true as i32; // 1
let de_int_para_char = 65u8 as char; // 'A'
println!("{} {} {} {} {}", x, y, z, de_int_para_float, de_float_para_int);
}
Lembre-se: em Rust, a segurança de tipos é uma característica fundamental. Aproveite o sistema de tipos para evitar erros comuns de programação que outras linguagens permitem.
Referências
- Tipos de Dados - Documentação Oficial Rust — Capítulo completo sobre tipos de dados do livro oficial, incluindo escalares
- Rust by Example: Primitives — Exemplos práticos de tipos primitivos em Rust
- Numeric Types - Rust Reference — Documentação detalhada dos tipos numéricos na referência da linguagem
- Rust's Built-in Types - The Rustonomicon — Discussão avançada sobre tipos internos e seus comportamentos
- Integer Overflow in Rust — Artigo técnico sobre overflow de inteiros e estratégias de mitigação
- Unicode in Rust — Documentação do tipo char e manipulação de caracteres Unicode