No description
Find a file
Jonatas Oliveira 0dd0d6144c fix(004): replace trivial is_empty tests with real assertions that verify inference concept
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.
2026-08-11 13:24:18 +02:00
.woodpecker fix: content audit corrections + CI migration to woodpecker + MSRV 1.85 2026-08-11 09:49:11 +02:00
src fix(004): replace trivial is_empty tests with real assertions that verify inference concept 2026-08-11 13:24:18 +02:00
tests fix(004): replace trivial is_empty tests with real assertions that verify inference concept 2026-08-11 13:24:18 +02:00
.gitignore Initial commit 2026-08-07 09:26:09 +00:00
Cargo.lock feat: inicial content 2026-08-07 11:27:21 +02:00
Cargo.toml fix: content audit corrections + CI migration to woodpecker + MSRV 1.85 2026-08-11 09:49:11 +02:00
LICENSE Initial commit 2026-08-07 09:26:09 +00:00
README.md fix(004): replace trivial is_empty tests with real assertions that verify inference concept 2026-08-11 13:24:18 +02:00

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:

  1. O valor atribuído: let x = 5;x é i32
  2. Como a variável é usada: let y = vec![1, 2, 3];y é Vec<i32>
  3. O tipo de retorno esperado: let s: String = "texto".into();.into() sabe que precisa retornar String

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 (i32 vs u64 vs f64)
  • 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 como i32 porque 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 é usize porque len() retorna usize
  • Linha 21-28: Duas formas de escrever a mesma função, uma com inferência total, outra com anotação intermediária

Missão

  1. Implementar processar_leitura_inferida: Recebe um valor numérico e retorna uma String que 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.
  2. Implementar processar_leitura_explicita: Mesmo valor de entrada, mas a saída deve mencionar o tipo i32 explicitamente (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.
  3. Implementar combinar_dados: Recebe dois valores i32 e retorna uma String contendo a soma a + b (ex.: combinar_dados(10, 20) → contém "30"). O tipo da variável intermediária da soma é inferido.
  4. Implementar validar_intervalo: Retorna true se valor ∈ [min, max] (limites inclusivos), false caso contrário.
  5. Implementar formatar_saida: Recebe um &str e retorna uma String formatada 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 i32 para 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