Pular para o conteúdo

Erro E0597 do Rust: borrowed value does not live long enough (vive pouco demais)

E0597 significa: você manteve uma referência (&x) por mais tempo do que o próprio valor x existe. O valor é descartado no fim do bloco dele, mas alguma coisa fora desse bloco ainda quer usar a referência. É um erro muito comum, principalmente com blocos internos, laços e structs, e a correção costuma ser mover uma linha de lugar ou guardar um valor próprio em vez de uma referência. (Esta página faz parte de Erros do compilador do Rust explicados.)

error[E0597]: `x` does not live long enough
--> src/main.rs:5:13
|
4 | let x = 5;
| - binding `x` declared here
5 | r = &x; // r pega x emprestado
| ^^ borrowed value does not live long enough
6 | } // x é descartado aqui
| - `x` dropped here while still borrowed
7 | println!("r: {r}"); // ❌ r apontaria para o nada
| - borrow later used here

O compilador diz que x não vive o suficiente (does not live long enough): ele é descartado (dropped) enquanto ainda está emprestado, e o empréstimo é usado depois. Este é o programa que causa o erro:

fn main() {
let r;
{
let x = 5;
r = &x; // r pega x emprestado
} // x é descartado aqui
println!("r: {r}"); // ❌ r apontaria para o nada
}

Os quatro rótulos contam a história em ordem:

  1. Linha 4: x é criado dentro do bloco interno { ... } (binding x declared here).
  2. Linha 5: r pega x emprestado. Até aqui, tudo bem.
  3. Linha 6: o bloco termina, então x é descartado (dropped, liberado da memória). O empréstimo continua vivo.
  4. Linha 7: r é usado (borrow later used here), mas o valor para o qual ele aponta já não existe.

Pense em anotar o número do quarto de hotel de um amigo. Isso é útil enquanto ele está hospedado lá. Depois que ele faz o check-out, o número continua no seu papel, mas agora leva a um quarto vazio, ou ao quarto de um desconhecido. O Rust não deixa você guardar um papel que leva a um quarto vazio.

Você também vai ver o rótulo curto “borrowed value does not live long enough” (o valor emprestado não vive o suficiente). É o mesmo erro; o título só cita o nome da variável.

Uma referência nunca pode viver mais que o valor para o qual aponta. Se vivesse, você teria uma dangling reference (referência pendente): um ponteiro para uma memória que já foi liberada e que agora pode guardar outra coisa. Em C e C++, isso é uma fonte clássica de travamentos e falhas de segurança (“use after free”).

No Rust, todo valor é descartado quando o seu dono sai do escopo, normalmente no } que fecha o bloco onde ele foi declarado. O borrow checker (o verificador de empréstimos) compara dois intervalos: quanto tempo o valor vive e por quanto tempo a referência é usada. Se a referência é usada depois do } do valor, você recebe o E0597. Veja Escopo, Borrowing e Lifetimes.

Repare no que o compilador não reclama: de pegar x emprestado na linha 5. O problema é só o uso posterior na linha 7. Apague essa última linha e o programa compila. Essa é uma pista útil para as correções abaixo: ou você faz o valor viver mais, ou faz o uso da referência terminar mais cedo.

Escolha esta quando o valor realmente é necessário por tanto tempo quanto a referência. É a correção mais comum: declare o valor antes (fora) do bloco, para que ele viva pelo menos tanto quanto a referência.

fn main() {
let scores = vec![72, 95, 88]; // agora vive até o fim de main
let best;
{
best = scores.iter().max().unwrap();
}
println!("Melhor nota: {best}"); // ✅ scores ainda está vivo
}

Saída:

Melhor nota: 95
▶ Experimente você mesmo

Regra prática: o dono precisa ser declarado antes de quem pega emprestado, e precisa viver mais que ele. Se você vê um valor criado dentro de um bloco ou de um laço e uma referência a ele usada do lado de fora, mova o valor para fora.

Correção 2: Guarde valores próprios em vez de referências

Seção intitulada “Correção 2: Guarde valores próprios em vez de referências”

Escolha esta quando você junta valores criados dentro de um laço. Cada volta do laço cria uma String nova e a descarta no fim da volta, então um Vec<&str> fora do laço não consegue guardar referências a elas:

fn main() {
let mut names: Vec<&str> = Vec::new();
for i in 1..=3 {
let name = format!("jogador{i}");
names.push(&name); // ❌ `name` does not live long enough
}
println!("{names:?}");
}

A correção é deixar o Vec ser dono das strings. Troque Vec<&str> por Vec<String> e coloque a própria String no vetor (isso a move para dentro do vetor, então ela vive tanto quanto ele):

fn main() {
let mut names: Vec<String> = Vec::new();
for i in 1..=3 {
let name = format!("jogador{i}");
names.push(name); // move a String para dentro do Vec
}
println!("{names:?}");
}

Saída:

["jogador1", "jogador2", "jogador3"]
▶ Experimente você mesmo

A mesma ideia vale para structs e HashMaps: se os dados são criados em um escopo menor que o do contêiner, guarde String (ou Vec<T>) em vez de &str (ou &[T]). Para quem está começando, campos próprios são quase sempre a escolha certa. Veja Strings e Vetores.

Escolha esta quando você também precisa continuar usando o original, ou quando só tem um empréstimo dele. Uma struct que guarda uma referência (Label<'a> { text: &'a str }) não pode viver mais que a string para a qual aponta. Dê à struct um campo String próprio e guarde uma cópia:

struct Label {
text: String, // próprio, não &str
}
fn main() {
let label;
{
let title = String::from("Inbox");
label = Label { text: title.clone() };
println!("Dentro do bloco: {title}");
}
println!("Rótulo: {}", label.text); // ✅ label tem a sua própria cópia
}

Saída:

Dentro do bloco: Inbox
Rótulo: Inbox
▶ Experimente você mesmo

Se você não precisa mais de title depois, dispense o clone e mova o valor: Label { text: title }. Clonar uma string curta é barato e não tem problema nenhum. Se você só tem um &str, use .to_string() para ter uma cópia própria.

Correção 4: Reorganize o código para o empréstimo terminar a tempo

Seção intitulada “Correção 4: Reorganize o código para o empréstimo terminar a tempo”

Escolha esta quando o valor é para ter vida curta mesmo. Em vez de fazer o valor viver mais, use a referência enquanto o valor ainda está vivo. Isso aparece muito com funções que devolvem uma referência, como o exemplo clássico longest:

fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() { a } else { b }
}
fn main() {
let first = String::from("Ferris the crab");
{
let second = String::from("Corro");
let winner = longest(&first, &second);
println!("Nome mais longo: {winner}"); // ✅ usado antes de second ser descartado
}
}

Saída:

Nome mais longo: Ferris the crab
▶ Experimente você mesmo

Se winner for declarado fora do bloco e impresso depois dele, você recebe o E0597 para second, mesmo que first seja o mais longo. A assinatura diz que o resultado pode vir emprestado de qualquer um dos argumentos, então os dois precisam viver mais que ele. Se você realmente precisa do resultado depois, guarde uma cópia própria: let winner = longest(&first, &second).to_string();.

Uma closure guardada fora do bloco. Uma closure (função anônima) que usa name pega name emprestado. Se a closure vive mais que name, você recebe o E0597 com um rótulo extra, value captured here (valor capturado aqui):

let say_hello;
{
let name = String::from("Ferris");
say_hello = || println!("Hello, {name}!"); // ❌ `name` does not live long enough
}
say_hello();

Correção: escreva move || ... para que a closure fique com o ownership (posse) de name. Veja Closures.

Threads. Pegar uma variável local emprestada dentro de thread::spawn(|| ...) dá outro erro, o E0373 (“closure may outlive the current function”, a closure pode viver mais que a função atual), porque a thread pode rodar por mais tempo que main. Correção: thread::spawn(move || ...), compartilhar com Arc ou usar std::thread::scope. Veja Concorrência.

Uma referência a um valor temporário. let label = Label { text: String::from("Inbox").as_str() }; é parecido com o E0597, mas aparece como E0716, “temporary value dropped while borrowed” (valor temporário descartado enquanto emprestado): a String nunca é guardada em uma variável, então é descartada no fim da instrução. Correção: coloque-a primeiro em uma variável (let title = String::from("Inbox");) ou transforme o campo em String.

Devolvendo uma referência de uma função. Se o valor é criado dentro da função, você recebe o E0515 no lugar. Devolva o valor próprio.

Teste o que aprendeu

1. Em let r; { let x = 5; r = &x; } seguido de um println! de r, o que de fato provoca o E0597?

2. Você cria uma String em cada volta do laço e coloca &name em um Vec<&str>. Qual é a correção mais comum?

3. A closure de uma thread pega emprestado um Vec local. Qual erro você recebe?

Pontuação: 0 / 3