- Rust 100%
All 5 String-returning tests previously only checked !resultado.is_empty(), letting a student pass with any non-empty String. The doc comment on processar_leitura_inferida also claimed the parameter type was 'inferred', which is impossible in Rust function signatures — parameters MUST be annotated. Changes: - tests/integracao.rs: assert specific content (value present, 'i32' type marker present in _explicita only, sum in combinar_dados, input preserved in formatar_saida) plus assert_ne! between inferida and explicita outputs to make mission 2 verifiable. - src/lib.rs: reframe docs to clarify inference happens INSIDE the body (variables, intermediate expressions, return type), not on parameters. Make _explicita doc state the output surfaces the i32 type. - README.md: add note about parameter annotation requirement, give concrete example outputs for each mission item so students know what to produce. |
||
|---|---|---|
| .woodpecker | ||
| src | ||
| tests | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE | ||
| README.md | ||
Lição 4: Inteligência do Sistema
Narrativa
Piloto, agora que você domina os controles básicos da cabine, é hora de conhecer o verdadeiro poder do seu mecha: a Inteligência do Sistema. Imagine um copiloto virtual que observa cada movimento seu e automaticamente ajusta os parâmetros internos para obter o melhor desempenho. Esse é o poder da inferência de tipos em Rust.
Na Academia Æther, chamamos isso de Sistema de Inferência Neural. Assim como um mecha avançado analisa o contexto da missão para decidir qual modo de operação usar, o compilador Rust analisa como você usa seus dados para determinar automaticamente os tipos mais apropriados. Mas atenção: em situações críticas, um piloto experiente sabe quando confiar na inteligência automática e quando assumir o controle manual.
Conceito
Inferência de tipos é a habilidade do compilador de deduzir automaticamente o tipo de uma variável baseado no contexto de uso. Em Rust, você pode frequentemente omitir anotações de tipo, e o compilador determinará o tipo correto analisando:
- O valor atribuído:
let x = 5;→xéi32 - Como a variável é usada:
let y = vec![1, 2, 3];→yéVec<i32> - O tipo de retorno esperado:
let s: String = "texto".into();→.into()sabe que precisa retornarString
Quando confiar na inferência:
- Código simples e óbvio
- Variáveis locais temporárias
- Contextos onde o tipo é evidente
Quando anotar explicitamente:
- APIs públicas (outros pilotos precisam saber os tipos)
- Números que podem ter múltiplos tipos válidos (
i32vsu64vsf64) - Código complexo onde a inferência pode ser ambígua
- Constantes e statics (obrigatório)
Requisitos
- Entrada: Valores numéricos e textuais para processamento
- Processamento: Inferência automática vs anotação explícita de tipos
- Saída: Valores processados com tipos corretamente resolvidos
Design de Dados
Esta lição explora como Rust resolve tipos em diferentes cenários:
// Inferência simples - tipo deduzido do valor literal
let velocidade = 100; // i32 inferido
// Inferência com contexto - tipo deduzido do uso
let texto = "Mecha Alpha"; // &str inferido
// Anotação explícita quando necessária
let precisao: f64 = 0.001; // f64 explicitamente
// Inferência em expressões complexas
let resultado = 10 + 20; // i32 inferido da operação
As funções demonstram quando o compilador pode inferir tipos automaticamente e quando o programador deve fornecer anotações explícitas.
Diagrama Conceitual
flowchart TD
A[Código Fonte] --> B{Análise do Contexto}
B -->|Valor Literal| C[Inferência Direta]
B -->|Uso da Variável| D[Inferência por Contexto]
B -->|Expressão Complexa| E[Inferência por Operação]
B -->|Múltiplos Tipos Possíveis| F[Anotação Explícita Necessária]
C --> G[Tipo Resolvido]
D --> G
E --> G
F --> H[Tipo Especificado pelo Piloto]
G --> I[Compilação Bem-Sucedida]
H --> I
Funções e Módulos
Nota: em Rust, parâmetros de função exigem anotação de tipo na assinatura — não há inferência nos parâmetros. A inferência atua dentro do corpo da função (variáveis locais, expressões intermediárias, tipo de retorno propagado pelo uso). As duas primeiras funções contrastam os dois estilos: confiar na inferência no corpo vs. tornar o tipo explicitamente visível na saída.
//! Lição 4: Inteligência do Sistema
//!
//! Entendendo a inferência de tipos da Academia Æther.
/// Inferência no corpo: variáveis locais têm tipos deduzidos do uso.
/// Saída reflete APENAS o valor — sem mencionar o tipo.
pub fn processar_leitura_inferida(valor: i32) -> String;
/// Anotação explícita visível: a saída menciona o tipo `i32`.
/// Demonstra quando explicitar é preferível (APIs públicas, ambiguidade numérica).
pub fn processar_leitura_explicita(valor: i32) -> String;
/// Combina dois dados: o resultado da operação tem tipo deduzido no corpo.
pub fn combinar_dados(a: i32, b: i32) -> String;
/// Valida intervalo: parâmetros anotados, resultado bool inferido da expressão.
pub fn validar_intervalo(valor: i32, min: i32, max: i32) -> bool;
/// Formata saída: o tipo String é propagado reversamente a partir do uso.
pub fn formatar_saida(dados: &str) -> String;
Exemplo do Conceito
// Exemplo 1: Inferência básica - o compilador deduz i32
let temperatura = 25;
let mensagem = format!("Temperatura: {}", temperatura);
// Exemplo 2: Inferência em coleções - deduz Vec<i32>
let leituras = vec![100, 200, 300];
// Exemplo 3: Quando explicitar é necessário - múltiplos tipos possíveis
let precisao: f64 = 0.001; // Sem anotação, seria ambíguo
let contador: u32 = 0; // Poderia ser i32, u8, etc.
// Exemplo 4: Inferência em closures - tipo dos parâmetros deduzido
let dobrar = |x| x * 2; // x é i32 se usado com i32
let resultado = dobrar(5); // resultado é i32
// Exemplo 5: Inferência com métodos genéricos
let texto = String::from("Mecha");
let tamanho = texto.len(); // usize inferido do retorno de len()
// Exemplo 6: Comparação inferido vs explícito
fn processar(valor: i32) -> String {
format!("Valor: {}", valor) // retorno String inferido
}
fn processar_explicito(valor: i32) -> String {
let resultado: String = format!("Valor: {}", valor);
resultado // tipo explícito na variável intermediária
}
Explicação:
- Linha 2:
temperaturaé inferido comoi32porque 25 é um literal inteiro padrão - Linha 6:
leituraséVec<i32>porque vec![] com inteiros cria esse tipo - Linha 9-10: Anotações explícitas necessárias quando múltiplos tipos são válidos
- Linha 13-14: Closures inferem tipos dos parâmetros baseado no primeiro uso
- Linha 18:
tamanhoéusizeporquelen()retornausize - Linha 21-28: Duas formas de escrever a mesma função, uma com inferência total, outra com anotação intermediária
Missão
- Implementar
processar_leitura_inferida: Recebe um valor numérico e retorna umaStringque contém o valor mas NÃO menciona o tipo (ex.:format!("Leitura: {}", valor)→"Leitura: 42"). O corpo deve confiar na inferência para variáveis locais. - Implementar
processar_leitura_explicita: Mesmo valor de entrada, mas a saída deve mencionar o tipoi32explicitamente (ex.:format!("Leitura: {}: i32", valor)→"Leitura: 42: i32"). Demonstre quando a anotação explícita é preferível — clareza em APIs públicas ou valores numéricos ambíguos. Os testes verificam que apenas esta função produz uma saída distinguível da inferida. - Implementar
combinar_dados: Recebe dois valoresi32e retorna umaStringcontendo a somaa + b(ex.:combinar_dados(10, 20)→ contém"30"). O tipo da variável intermediária da soma é inferido. - Implementar
validar_intervalo: Retornatruesevalor ∈ [min, max](limites inclusivos),falsecaso contrário. - Implementar
formatar_saida: Recebe um&stre retorna umaStringformatada que preserva o conteúdo recebido (ex.:formatar_saida("dados de voo")→ contém"dados de voo").
Diagrama de Fluxo da Missão
flowchart TD
A[Tarefa: Inferência de Tipos] --> B[Implementar processar_leitura_inferida]
B --> C[Implementar processar_leitura_explicita]
C --> D[Implementar combinar_dados]
D --> E[Implementar validar_intervalo]
E --> F[Implementar formatar_saida]
F --> G[Testes verificam inferência]
G --> H{Clippy limpo?}
H -->|Sim| I[Concluído]
H -->|Não| J[Corrigir warnings] --> G
Como Executar
# Executar testes
cargo test
# Verificar lint
cargo clippy -- -D warnings
# Formatar código
cargo fmt --check
# Verificar compilação sem rodar
cargo check
Dicas
- Rust infere
i32para inteiros literais por padrão - Use anotações explícitas em funções públicas da API
- A inferência funciona para frente e para trás no código
- Em caso de ambiguidade, o compilador sugere anotações
_pode ser usado para ignorar valores sem tipo específico- Constantes (
const) sempre requerem tipo explícito - A inferência não afeta performance — o tipo é determinado em tempo de compilação