Agora que você tem o Rust instalado, é hora de escrever seu primeiro programa! Neste tutorial, vamos explorar o Cargo (o gerenciador de projetos do Rust), criar nosso primeiro “Hello, World!” e entender como os projetos Rust são organizados.
Seu Primeiro Programa: Hello, World!
Vamos começar da forma mais simples possível. Crie um arquivo chamado main.rs em qualquer pasta:
fn main() {
println!("Olá, mundo!");
}
Compile e execute diretamente com o rustc:
rustc main.rs
./main
Saída:
Olá, mundo!
Vamos entender cada parte:
fn main()— Define a função principal do programa. Todo programa Rust começa pela funçãomain.println!— É uma macro (note o!no final) que imprime texto no terminal com uma quebra de linha. Macros em Rust são indicadas pelo!e são diferentes de funções normais."Olá, mundo!"— Uma string literal entre aspas duplas.
Embora compilar com rustc funcione, para projetos reais você sempre vai usar o Cargo.
Conhecendo o Cargo
O Cargo é muito mais do que um compilador. Ele é o canivete suíço do desenvolvedor Rust:
- Gerenciador de projetos — Cria e organiza a estrutura do projeto
- Build system — Compila seu código e suas dependências
- Gerenciador de dependências — Baixa e gerencia bibliotecas (chamadas de crates)
- Executor de testes — Roda testes unitários e de integração
- Gerador de documentação — Gera documentação a partir de comentários no código
Criando um Projeto com Cargo
Para criar um novo projeto, use cargo new:
cargo new meu-projeto
cd meu-projeto
O Cargo cria a seguinte estrutura:
meu-projeto/
├── Cargo.toml
├── .gitignore
└── src/
└── main.rs
Vamos entender cada arquivo.
Cargo.toml
O Cargo.toml é o arquivo de configuração do projeto. Ele usa o formato TOML (Tom’s Obvious, Minimal Language):
[package]
name = "meu-projeto"
version = "0.1.0"
edition = "2021"
[dependencies]
[package]— Informações sobre o seu projetoname— Nome do projeto (usado pelo Cargo e pelo crates.io)version— Versão seguindo Semantic Versioningedition— Edição do Rust (2015, 2018, 2021 ou 2024). Use sempre a mais recente
[dependencies]— Lista de dependências externas (vazia por enquanto)
src/main.rs
O arquivo principal do projeto já vem com o “Hello, World!”:
fn main() {
println!("Hello, world!");
}
Vamos personalizá-lo:
fn main() {
let nome = "Rustáceo";
let linguagem = "Rust";
println!("Olá, {}! Bem-vindo ao {}!", nome, linguagem);
println!("Versão do programa: {}", env!("CARGO_PKG_VERSION"));
}
A macro println! aceita placeholders {} que são substituídos pelos valores na ordem em que aparecem. A macro env! acessa variáveis de ambiente em tempo de compilação — neste caso, a versão definida no Cargo.toml.
Comandos Essenciais do Cargo
cargo build
Compila o projeto sem executá-lo:
cargo build
O executável é gerado em target/debug/meu-projeto. A compilação no modo debug inclui informações de depuração e não aplica otimizações, o que torna a compilação mais rápida.
Para compilar em modo release (com otimizações):
cargo build --release
O executável otimizado fica em target/release/meu-projeto. Use o modo release para distribuição e benchmarks.
cargo run
Compila e executa o projeto em um só comando:
cargo run
Saída:
Compiling meu-projeto v0.1.0 (/home/usuario/meu-projeto)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.54s
Running `target/debug/meu-projeto`
Olá, Rustáceo! Bem-vindo ao Rust!
Versão do programa: 0.1.0
Se o código não foi alterado desde a última compilação, o Cargo não recompila — ele simplesmente executa o binário existente. Isso torna o cargo run muito eficiente no dia a dia.
cargo check
Verifica se o código compila sem gerar o executável:
cargo check
Este comando é muito mais rápido que cargo build porque pula a etapa de geração de código. Use-o frequentemente durante o desenvolvimento para verificar erros rapidamente.
cargo test
Executa os testes do projeto:
cargo test
Vamos criar um teste simples. Modifique o src/main.rs:
fn somar(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let resultado = somar(3, 4);
println!("3 + 4 = {}", resultado);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn teste_somar() {
assert_eq!(somar(2, 3), 5);
assert_eq!(somar(-1, 1), 0);
assert_eq!(somar(0, 0), 0);
}
#[test]
fn teste_somar_negativos() {
assert_eq!(somar(-5, -3), -8);
}
}
Execute cargo test:
Compiling meu-projeto v0.1.0 (/home/usuario/meu-projeto)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.62s
Running unittests src/main.rs (target/debug/deps/meu-projeto-abc123)
running 2 tests
test tests::teste_somar ... ok
test tests::teste_somar_negativos ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Vamos entender a estrutura de testes:
#[cfg(test)]— Este módulo só é compilado quando rodamos testesmod tests— Um módulo para agrupar os testesuse super::*— Importa tudo do módulo pai (ondesomarestá definida)#[test]— Marca uma função como testeassert_eq!— Macro que verifica se dois valores são iguais
cargo fmt
Formata o código seguindo o estilo oficial do Rust:
cargo fmt
Isso garante que todo o código do projeto siga um estilo consistente. Não há discussão sobre tabs vs. espaços ou onde colocar as chaves — o rustfmt decide por você!
cargo clippy
Roda o linter Clippy, que dá sugestões inteligentes para melhorar seu código:
cargo clippy
O Clippy detecta padrões comuns que podem ser simplificados, potenciais bugs e código idiomático. É altamente recomendado rodar o Clippy regularmente.
Adicionando Dependências
Uma das maiores forças do ecossistema Rust é o crates.io, o repositório oficial de bibliotecas. Vamos adicionar uma dependência como exemplo.
Adicione a crate colored ao Cargo.toml:
[package]
name = "meu-projeto"
version = "0.1.0"
edition = "2021"
[dependencies]
colored = "2"
Ou use o comando cargo add (se você instalou o cargo-edit):
cargo add colored
Agora use no seu código:
use colored::*;
fn main() {
println!("{}", "Olá, Rust Brasil!".green().bold());
println!("{}", "Este texto é vermelho!".red());
println!("{}", "E este é azul com fundo amarelo!".blue().on_yellow());
}
Na próxima vez que executar cargo run, o Cargo vai baixar e compilar a dependência automaticamente.
Projetos Binários vs. Bibliotecas
O Cargo suporta dois tipos de projetos:
Projeto binário (executável)
cargo new meu-app # cria um projeto binário (padrão)
Contém src/main.rs com a função main().
Projeto de biblioteca
cargo new minha-lib --lib # cria um projeto de biblioteca
Contém src/lib.rs em vez de src/main.rs:
/// Soma dois números inteiros.
///
/// # Exemplos
///
/// ```
/// let resultado = minha_lib::somar(2, 3);
/// assert_eq!(resultado, 5);
/// ```
pub fn somar(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn funciona() {
assert_eq!(somar(2, 2), 4);
}
}
Note o comentário com /// — isso é um doc comment. Você pode gerar documentação HTML com:
cargo doc --open
Estrutura de um Projeto Real
Conforme seu projeto cresce, a estrutura se expande:
meu-projeto/
├── Cargo.toml
├── Cargo.lock # Versões exatas das dependências (gerado automaticamente)
├── .gitignore
├── src/
│ ├── main.rs # Ponto de entrada do binário
│ ├── lib.rs # Código da biblioteca (opcional)
│ └── modulo/
│ ├── mod.rs # Declaração do módulo
│ └── funcoes.rs # Funções do módulo
├── tests/
│ └── integration_test.rs # Testes de integração
├── benches/
│ └── benchmark.rs # Benchmarks
└── examples/
└── exemplo.rs # Exemplos executáveis
Cargo.lock— Gerado automaticamente pelo Cargo. Registra as versões exatas de todas as dependências. Inclua no Git para projetos binários.tests/— Testes de integração que testam seu código como um usuário externo faria.examples/— Exemplos que podem ser executados comcargo run --example exemplo.benches/— Benchmarks de performance.
Exercício Prático
Crie um projeto que funciona como uma calculadora simples:
fn somar(a: f64, b: f64) -> f64 {
a + b
}
fn subtrair(a: f64, b: f64) -> f64 {
a - b
}
fn multiplicar(a: f64, b: f64) -> f64 {
a * b
}
fn dividir(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None
} else {
Some(a / b)
}
}
fn main() {
let a = 10.0;
let b = 3.0;
println!("Calculadora Rust");
println!("================");
println!("{} + {} = {}", a, b, somar(a, b));
println!("{} - {} = {}", a, b, subtrair(a, b));
println!("{} * {} = {}", a, b, multiplicar(a, b));
match dividir(a, b) {
Some(resultado) => println!("{} / {} = {:.2}", a, b, resultado),
None => println!("Erro: divisão por zero!"),
}
// Testando divisão por zero
match dividir(a, 0.0) {
Some(resultado) => println!("{} / 0 = {:.2}", a, resultado),
None => println!("Erro: divisão por zero!"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn teste_somar() {
assert_eq!(somar(2.0, 3.0), 5.0);
}
#[test]
fn teste_dividir() {
assert_eq!(dividir(10.0, 2.0), Some(5.0));
}
#[test]
fn teste_dividir_por_zero() {
assert_eq!(dividir(10.0, 0.0), None);
}
}
Execute com cargo run e teste com cargo test para ver tudo funcionando.
Próximos Passos
Agora que você sabe criar e gerenciar projetos com o Cargo, é hora de aprender os fundamentos da linguagem. No próximo tutorial, vamos explorar variáveis, tipos primitivos e funções em Rust.
Acesse o tutorial Variáveis, Tipos e Funções para continuar.