Desafio Rust

Referências pendentes e escopo

Enunciado

Escreva um programa que demonstre (e depois corrija) uma tentativa de criar uma referência pendente. Crie uma função criar_referencia_invalida que tenta retornar uma referência para uma variável local. O código não deve compilar; comente-o e mostre a versão corrigida que retorna uma String própria. Explique no código com comentários o que o compilador acusaria.

Requisitos

  • O código deve conter uma função que tenta retornar &String de uma variável local (comentada ou em bloco #[allow(dead_code)]).
  • Deve haver uma versão corrigida que retorna String (não referência).
  • O main deve chamar a versão corrigida e imprimir o resultado.
  • Comentários devem explicar por que a versão original não compila.

Código inicial

// Tente criar uma função que retorna &String de uma variável local
// e depois corrija retornando String.

fn main() {
    // chame a versão corrigida
}

Saída esperada

Valor corrigido: Rust
Ver dica

Uma referência não pode viver mais que o valor que ela aponta. Se a variável é local, ela é destruída ao final da função, então a referência ficaria pendente.

Mostrar solução
// Esta função NÃO compila: tenta retornar uma referência para uma variável local.
// O compilador acusaria: `missing lifetime specifier` ou
// `returns a value referencing data owned by the current function`.
// fn criar_referencia_invalida() -> &String {
//     let s = String::from("Rust");
//     &s // s é destruída no final da função; a referência ficaria pendente
// }

// Versão corrigida: retorna uma String própria (move o valor para o chamador).
fn criar_string_valida() -> String {
    let s = String::from("Rust");
    s // move a String para quem chamou
}

fn main() {
    let valor = criar_string_valida();
    println!("Valor corrigido: {}", valor);
}

Passo a passo

  1. A função criar_referencia_invalida (comentada) tenta retornar &String de uma variável local s.
  2. O compilador do Rust detecta que s é destruída ao final da função, então a referência ficaria apontando para memória inválida — uma dangling reference.
  3. O erro típico é: missing lifetime specifier ou returns a value referencing data owned by the current function.
  4. Para corrigir, a função criar_string_valida retorna String (o tipo owned). Assim, a posse é transferida para o chamador.
  5. No main, let valor = criar_string_valida(); recebe a String e pode usá-la com segurança.
  6. O comentário no código explica o motivo do erro, cumprindo o requisito de demonstrar a referência pendente.

Por que funciona

O borrow checker impede referências pendentes ao garantir que o tempo de vida de uma referência não ultrapasse o tempo de vida do valor referenciado. Ao retornar uma String própria, transferimos a posse, e o valor vive no escopo do chamador, sem dependência de variáveis locais.

Erros comuns

  • Retornar &String de variável local: o compilador acusa missing lifetime specifier ou returns a value referencing data owned by the current function. A correção é retornar String.
  • Tentar usar a referência após o dono sair de escopo:
    let r;
    {
        let s = String::from("x");
        r = &s;
    }
    println!("{}", r); // erro: `s` does not live long enough
    
    A solução é garantir que o dono viva tanto quanto a referência.
  • Criar múltiplos empréstimos mutáveis: let r1 = &mut s; let r2 = &mut s; causa erro cannot borrow s as mutable more than once at a time.

Outra forma de resolver

Usar String::from diretamente no retorno, sem variável local:

fn criar_string_valida() -> String {
    String::from("Rust")
}

Saída esperada

Valor corrigido: Rust