Noções Básicas de Rust
10 exemplos para você começar com os fundamentos de Rust - 7 básicos e 3 intermediários.
Pré-requisitos
- Rust 1.97.0 com edição 2024 (
rustup default stable) cargo --versionimprime 1.97 ou mais recente
Exemplos Básicos
1. Olá com main
Todo programa Rust começa em main.
fn main() {
println!("Olá, Rust!");
}cargo new hellocria um esqueleto de crate bináriocargo runcompila e executaprintln!é uma macro, não uma função- Ponto e vírgulas terminam declarações; expressões sem
;são valores retornados
Relacionado: Formatação e Impressão -
Displaye especificadores de formato
2. Bindings Imutáveis e Mutáveis
Variáveis são imutáveis, a menos que você opte por mut.
fn main() {
let x = 5;
// x = 6; // erro de compilação
let mut count = 0;
count += 1;
println!("{count}");
}letcria um binding; imutabilidade é o padrãomutpermite reatribuição do binding- Imutabilidade não significa que o valor por trás de uma referência não pode mudar
- Prefira
letsemmutaté que a mutação seja necessária
Relacionado: Variáveis, Mutabilidade e Sombreamento - sombreamento e constantes
3. Tipos Escalares
Rust tem inteiros de tamanho fixo, floats, bool e char (escalar Unicode).
fn main() {
let age: u32 = 30;
let ratio: f64 = 0.75;
let active: bool = true;
let letter: char = 'R';
println!("{age} {ratio} {active} {letter}");
}- Tipos inteiros:
i8..i128,u8..u128,isize,usize - Inferência de tipo preenche os tipos quando não ambígua
chartem 4 bytes (Unicode), não 1 byte como C- Literais suportam sufixos:
42u64,3.14f32
Relacionado: Tipos Escalares e Compostos - tuplas e arrays
4. Funções e Valores de Retorno
A última expressão em um bloco é o valor de retorno.
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let sum = add(2, 3);
println!("{sum}");
}- Tipos de parâmetro e tipo de retorno são obrigatórios (exceto para
main) returné explícito, mas raramente necessário- Um
;final transforma uma expressão em uma declaração (descarta o valor) - Funções são nomes de primeira classe, não métodos em objetos
Relacionado: Funções e Expressões - expressões vs declarações
5. if como Expressão
Ramificações podem produzir valores quando ambos os braços retornam o mesmo tipo.
fn main() {
let n = 7;
let label = if n % 2 == 0 { "even" } else { "odd" };
println!("{n} is {label}");
}if/elsedeve retornar tipos compatíveis em todos os braços- Sem "truthiness" implícita: condições devem ser
bool - Use
if letpara correspondência de padrão único (abordado mais tarde) - Prefira expressões a
lettemporário + mutação
Relacionado: Fluxo de Controle - loops e
match
6. Prévia de Posse (Ownership)
Cada valor tem um único dono; atribuição move a posse para tipos não-Copy.
fn main() {
let s1 = String::from("hello");
let s2 = s1; // move - s1 não é mais válido
// println!("{s1}"); // erro: valor movido
println!("{s2}");
}Stringpossui dados no heap;&strempresta uma fatia de string- Moves previnem double-free sem um garbage collector
- Inteiros na pilha (
i32) implementamCopye duplicam em vez de mover - Emprestar com
&compartilha acesso de leitura sem transferir a posse
Relacionado: Prévia de Posse (Ownership). - moves e borrows em profundidade
7. Referências e Empréstimos (Borrowing)
Empreste com & para acesso somente leitura, &mut para mutação exclusiva.
fn len(s: &String) -> usize {
s.len()
}
fn main() {
let mut text = String::from("rust");
println!("{}", len(&text));
text.push_str("ace");
println!("{text}");
}- Um
&mutou muitos¶ os mesmos dados ao mesmo tempo - Referências não devem sobreviver aos dados a que apontam
- Fatias (
&[T],&str) são ponteiros "gordos" para sequências contíguas - O verificador de empréstimos (borrow checker) impõe essas regras em tempo de compilação
Relacionado: Referências e Fatias - sintaxe de fatia e limites
Exemplos Intermediários
8. Pattern Matching com match
match é exaustivo: cada variante deve ser tratada.
enum Status {
Ok,
Err(u16),
}
fn describe(s: Status) -> &'static str {
match s {
Status::Ok => "success",
Status::Err(code) => {
println!("error code: {code}");
"failure"
}
}
}
fn main() {
println!("{}", describe(Status::Ok));
}_é um braço wildcard para padrões "catch-all"- Bindings (
code) extraem valores internos de variantes de enum matchemOption/Resulté o caminho idiomático para erros- O compilador gera erro se você esquecer uma variante
Relacionado: Fluxo de Controle -
if lete loops
9. Result para Erros Recuperáveis
Funções que podem falhar retornam Result<T, E>.
use std::fs;
fn read_config(path: &str) -> Result<String, std::io::Error> {
fs::read_to_string(path)
}
fn main() {
match read_config("config.toml") {
Ok(body) => println!("{body}"),
Err(e) => eprintln!("failed: {e}"),
}
}Ok(T)carrega o sucesso;Err(E)carrega a falha?propaga erros em funções que retornamResult- Panics (
panic!,unwrap) são para bugs irrecuperáveis - Bibliotecas usam erros tipados; binários frequentemente usam
anyhow
Relacionado: Prévia de Posse (Ownership). - tipos de erro também são enums
10. Um Teste Unitário Mínimo
Testes vivem no mesmo arquivo ou em diretórios de integração tests/.
pub fn double(n: i32) -> i32 {
n * 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn doubles_values() {
assert_eq!(double(3), 6);
}
}#[test]marca uma função paracargo testassert_eq!compara a saída de depuração em caso de falha- Testes unitários compartilham a API privada do módulo
- Testes de integração em
tests/*.rsveem apenas a API pública
Relacionado: Melhores Práticas de Fundamentos de Rust - hábitos antes de tópicos avançados
Versões da Pilha: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.