💻 Exemplos: Capítulo 06: Borrowing, Referências Imutáveis e Mutáveis (&, &mut)
💻 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:
- 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). - 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]