💻 Exemplos: Capítulo 06: Borrowing e Regras do Borrow Checker

Especialização em Sistemas com Rust • Demonstrações Práticas Compiláveis


🎯 Objetivo dos Exemplos

Demonstrar como o mecanismo de Borrowing (empréstimo de dados por referência sem transferir o ownership) permite compartilhar estruturas pesadas sem cópias desnecessárias na Heap, e comprovar as duas regras fundamentais do Borrow Checker:

  1. Em qualquer momento dado, você pode ter ou qualquer número de referências imutáveis (&T), ou exatamente uma referência mutável (&mut T).
  2. Referências devem ser sempre válidas (proibição de dangling pointers).

1. Código Fonte Completo (src/main.rs)

//! Demonstração de Empréstimos Imutáveis e Mutáveis com o Borrow Checker do Rust

// Função que toma emprestado por referência imutável (&String)
// O ownership NÃO é transferido. O dado pode continuar sendo lido por quem chamou.
fn calcular_comprimento(texto: &String) -> usize {
    texto.len()
}

// Função que toma emprestado por referência mutável (&mut String)
// Modifica o dado original diretamente na Heap sem alocar uma nova String
fn anexar_timestamp_log(registro: &mut String, timestamp: u64) {
    registro.push_str(&format!(" [TIMESTAMP: {} ms]", timestamp));
}

fn main() {
    println!("=== REGRAS DE OURO DO BORROW CHECKER ===");

    // 1. Empréstimo Imutável Múltiplo (Shared References)
    let buffer_log = String::from("KERNEL_BOOT_COMPLETED");

    // Múltiplos leitores podem coexistir simultaneamente em paz
    let ref1 = &buffer_log;
    let ref2 = &buffer_log;
    println!("Ref1: '{}' | Ref2: '{}'", ref1, ref2);
    
    let tamanho = calcular_comprimento(&buffer_log);
    println!("Tamanho do buffer lido por referência: {} bytes", tamanho);
    // 'buffer_log' continua 100% válido aqui!

    // 2. Empréstimo Mutável Exclusivo (Mutable Reference)
    let mut log_sistema = String::from("ERR_CONN_TIMEOUT");
    println!("\nLog antes da modificação: {}", log_sistema);

    // Passagem de referência mutável &mut
    anexar_timestamp_log(&mut log_sistema, 1698745230);
    println!("Log após mutação in-place: {}", log_sistema);

    // 3. Non-Lexical Lifetimes (NLL) em Ação
    // O Rust moderno sabe exatamente quando uma referência deixa de ser usada
    let mut buffer_editavel = String::from("Alpha");

    let leitor1 = &buffer_editavel;
    let leitor2 = &buffer_editavel;
    println!("\nLeitura compartilhada: {} e {}", leitor1, leitor2);
    // A partir desta linha, 'leitor1' e 'leitor2' não são mais lidos. O empréstimo imutável encerra aqui!

    // Agora o compilador permite criar com segurança uma referência mutável exclusiva!
    let escritor = &mut buffer_editavel;
    escritor.push_str(" -> Beta");
    println!("Valor após escrita exclusiva: {}", escritor);

    // 4. Exemplo de Prevenção de Inconsistência de Coleção
    let mut sensores = vec![10, 20, 30];
    let primeiro = &sensores[0]; // Empréstimo imutável do primeiro elemento
    println!("\nPrimeiro sensor: {}", primeiro);

    // Se tentássemos fazer `sensores.push(40)` enquanto 'primeiro' estivesse em uso,
    // o vetor poderia realocar na Heap, invalidando o ponteiro de 'primeiro'!
    // O compilador do Rust previne esse bug catastrófico em tempo de compilação.
    sensores.push(40); // Permitido aqui pois 'primeiro' não é mais lido após a linha anterior.
    println!("Vetor expandido com segurança: {:?}", sensores);
}

2. Saída Esperada no Terminal

=== REGRAS DE OURO DO BORROW CHECKER ===
Ref1: 'KERNEL_BOOT_COMPLETED' | Ref2: 'KERNEL_BOOT_COMPLETED'
Tamanho do buffer lido por referência: 21 bytes

Log antes da modificação: ERR_CONN_TIMEOUT
Log após mutação in-place: ERR_CONN_TIMEOUT [TIMESTAMP: 1698745230 ms]

Leitura compartilhada: Alpha e Alpha
Valor após escrita exclusiva: Alpha -> Beta

Primeiro sensor: 10
Vetor expandido com segurança: [10, 20, 30, 40]