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.)
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0597]: `x` does not live long enough --> src/main.rs:5:13 |4 | let x = 5; | - binding `x` declared here5 | r = &x; // r pega x emprestado | ^^ borrowed value does not live long enough6 | } // x é descartado aqui | - `x` dropped here while still borrowed7 | println!("r: {r}"); // ❌ r apontaria para o nada | - borrow later used hereO 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}O que significa
Seção intitulada “O que significa”Os quatro rótulos contam a história em ordem:
- Linha 4:
xé criado dentro do bloco interno{ ... }(bindingxdeclared here). - Linha 5:
rpegaxemprestado. Até aqui, tudo bem. - Linha 6: o bloco termina, então
xé descartado (dropped, liberado da memória). O empréstimo continua vivo. - 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.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”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.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Mova o valor para um escopo externo
Seção intitulada “Correção 1: Mova o valor para um escopo externo”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: 95Regra 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"]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.
Correção 3: Clone o valor
Seção intitulada “Correção 3: Clone o valor”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: InboxRótulo: InboxSe 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 crabSe 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();.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”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.
Erros relacionados
Seção intitulada “Erros relacionados”- E0515: cannot return reference to local variable (não é possível devolver referência a uma variável local): o mesmo problema, mas atravessando o limite de uma função.
- E0373: closure may outlive the current function: a versão deste erro com threads. Normalmente se resolve com
move. - E0106: missing lifetime specifier (falta o especificador de lifetime): o Rust precisa saber de onde vem o empréstimo de uma referência devolvida ou guardada.
- E0382: borrow of moved value (uso de valor movido): a armadilha oposta, usar um valor depois de entregá-lo.
- E0502: mutable borrow while immutably borrowed (empréstimo mutável durante um imutável): uma referência ainda está em uso quando você tenta mudar o valor.
E0716: temporary value dropped while borrowed: como o E0597, mas para um valor que nunca foi guardado em uma variável.- Todos os erros do compilador explicados
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”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
Para saber mais
Seção intitulada “Para saber mais”- E0597 — Rust error codes index (em inglês): a explicação oficial deste erro.
- The Borrow Checker — The Rust Book (em inglês): o mesmo exemplo com
rex, com um diagrama dos dois lifetimes. - Borrowing — Rust by Example (em inglês): exemplos curtos e executáveis de empréstimos e de quanto tempo eles duram.
- Lifetimes — Rust by Example (em inglês): como o compilador enxerga a vida de cada valor e de cada empréstimo.
- E0716 — Rust error codes index (em inglês): o erro parecido “temporary value dropped while borrowed”.