Erro E0716 do Rust: temporary value dropped while borrowed (valor temporário)
E0716 significa: você pegou emprestado um valor que não tem nome de variável e continuou usando esse empréstimo em uma linha seguinte. O Rust joga fora esses valores sem nome no fim da instrução, então o empréstimo apontaria para o nada. A mensagem assusta, mas é um dos erros de borrowing (empréstimo) mais fáceis de corrigir: normalmente basta dividir uma linha em duas. (Esta página faz parte de Erros do compilador do Rust explicados.)
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0716]: temporary value dropped while borrowed --> src/main.rs:2:13 |2 | let s = String::from("hi").as_str(); // s pega emprestada uma String sem nome | ^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | creates a temporary value which is freed while still in use3 | println!("{s}"); // ❌ essa String já não existe | - borrow later used here |help: consider using a `let` binding to create a longer lived value |2 ~ let binding = String::from("hi");3 ~ let s = binding.as_str(); // s pega emprestada uma String sem nome |O compilador diz que um valor temporário foi descartado enquanto ainda estava emprestado (temporary value dropped while borrowed) e sugere criar uma variável com let para que ele viva mais. Este é o programa que causa o erro:
fn main() { let s = String::from("hi").as_str(); // s pega emprestada uma String sem nome println!("{s}"); // ❌ essa String já não existe}O que significa
Seção intitulada “O que significa”Um temporário é um valor criado no meio de uma expressão, mas que nunca é guardado em uma variável. Na linha acima, String::from("hi") cria uma String de verdade, mas ninguém é dono dela: não existe um let nome = ... para ela. O Rust descarta (libera) um temporário no fim da instrução, ou seja, no ;.
Leia os três rótulos da esquerda para a direita:
^^^^embaixo deString::from("hi"): aqui é criada umaStringtemporária (creates a temporary value).-no fim da linha: aqui, no;, essaStringé liberada (freed at the end of this statement).-embaixo desna linha 3: mass(um&strque aponta para dentro dessaString) ainda é usado aqui (borrow later used here).
Pense em um garçom que anota o seu pedido em um papelzinho e joga o papel fora assim que sai da mesa. Se você diz a um amigo “o pedido está naquele papel”, seu amigo não encontra nada. Você precisa guardar o papel você mesmo, ou anotar o pedido em algum lugar que fique.
A parte help: da mensagem costuma ser a resposta: crie uma variável (o rustc a chama de binding) para que o valor tenha um dono que viva mais tempo.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”É a mesma regra do E0597: uma referência nunca pode viver mais que o valor para o qual aponta, senão ela vira uma referência pendente para uma memória já liberada. A única diferença é que, no E0597, o valor tem nome e morre em um }, enquanto no E0716 o valor não tem nome e morre no ;.
Usar um temporário dentro da mesma instrução não tem problema nenhum. println!("{}", String::from("hi").as_str()); compila, porque o empréstimo termina antes do ;. O erro só aparece quando o empréstimo ainda é necessário em uma linha seguinte. Veja Quando um borrow termina? e Lifetimes.
Existe uma exceção útil, chamada temporary lifetime extension (extensão do lifetime de temporários). Se você coloca & (ou &mut) diretamente na frente do temporário em um let, o Rust mantém o temporário vivo, sem alarde, pelo mesmo tempo que a variável. Por isso estas linhas compilam:
struct Label<'a> { text: &'a str,}
fn make_title() -> String { String::from("Inbox")}
fn main() { let greeting = &String::from("hi"); // ✅ estendido: & direto no let let queue = &mut vec![1, 2, 3]; // ✅ estendido queue.push(4); let label = Label { text: &make_title() }; // ✅ estendido: & direto em um literal de struct println!("{greeting} {queue:?} {}", label.text);}Saída:
hi [1, 2, 3, 4] InboxA extensão não atravessa uma chamada de método nem uma chamada de função. String::from("hi").as_str(), make_title().as_str(), vec![1, 2].iter() e shout(&make_title()) pegam o temporário emprestado dentro de uma chamada, então o temporário continua morrendo no ; e você recebe o E0716. Não tente decorar todos os casos: se aparecer o E0716, dê um nome ao valor.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Dê um nome ao temporário com let
Seção intitulada “Correção 1: Dê um nome ao temporário com let”Escolha esta quase sempre. É o que o compilador sugere, e é a resposta certa para a maioria dos E0716. Guarde o valor em uma variável própria primeiro e depois pegue emprestado dessa variável na linha seguinte.
Um caso muito comum é dividir uma linha de entrada. read_command() devolve uma String nova, .trim() e .split(' ') pegam emprestado dela, e o Vec<&str> guarda esses empréstimos:
let parts: Vec<&str> = read_command().trim().split(' ').collect(); // ❌ E0716O Rust aponta para read_command() e diz que ele é liberado no fim da instrução. Dê um nome a ele:
fn read_command() -> String { // Num programa de verdade, isto leria uma linha do stdin. String::from(" adicionar leite ovos \n")}
fn main() { let line = read_command(); // agora a String vive até o fim de main let parts: Vec<&str> = line.trim().split(' ').collect(); println!("{parts:?}"); println!("comando: {}, itens: {}", parts[0], parts.len() - 1);}Saída:
["adicionar", "leite", "ovos"]comando: adicionar, itens: 2Você vai esbarrar exatamente nisso com entrada de verdade também: lines.next().unwrap().unwrap().trim().split(' ').collect() em io::stdin().lines() dá E0716 pelo mesmo motivo. Coloque let line = lines.next().unwrap().unwrap(); em uma linha separada antes. Veja Strings.
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 o empréstimo precisa viver mais que qualquer variável que você poderia criar, por exemplo em uma struct que você passa adiante ou em um Vec que você preenche dentro de um laço. Um let dentro de um laço seria descartado no fim de cada volta, o que só transforma o E0716 em E0597.
Aqui estão duas versões com erro. Uma struct que guarda um &str de um temporário, e um laço que coloca no vetor empréstimos de strings novas:
let label = Label { text: make_title().as_str() }; // ❌ E0716
let mut names: Vec<&str> = Vec::new();for name in ["ferris", "corro"] { names.push(name.to_uppercase().as_str()); // ❌ E0716}A correção é deixar a struct e o Vec serem donos das suas strings. Use String em vez de &str e tire o .as_str():
struct Label { text: String, // próprio, então não precisa de lifetime}
fn make_title() -> String { String::from("Inbox")}
fn main() { let label = Label { text: make_title() }; // a String é movida para dentro do Label println!("Rótulo: {}", label.text);
let mut names: Vec<String> = Vec::new(); for name in ["ferris", "corro"] { names.push(name.to_uppercase()); // o Vec é dono de cada String nova } println!("{names:?}");}Saída:
Rótulo: Inbox["FERRIS", "CORRO"]Para quem está começando, campos próprios (String, Vec<T>) são quase sempre a escolha mais fácil. Uma struct com um campo &str precisa de um lifetime e nunca pode viver mais que os dados que pegou emprestado. Veja Structs que guardam referências e Structs.
Correção 3: Reorganize a cadeia
Seção intitulada “Correção 3: Reorganize a cadeia”Escolha esta quando o temporário é o começo de uma cadeia de iteradores. Estas linhas criam um Vec com vec![...], o pegam emprestado com .iter() e guardam o iterador (ou o resultado dele) para depois:
let doubled = &mut vec![1, 2, 3].iter().map(|n| n * 10); // ❌ E0716let best = vec![72, 95, 88].iter().max(); // ❌ E0716O &mut na frente não ajuda: a extensão de lifetime mantém vivo o iterador, mas não o Vec que .iter() pega emprestado. Há duas saídas:
- Use
into_iter()para que o iterador seja dono doVec(o rustc também sugere isso). Aí você nem precisa do&mut, só delet mut. - Termine a cadeia com um resultado próprio, como
.copied(),.cloned(),.sum()ou.collect::<Vec<_>>()de números simples, para que nada pegue o temporário emprestado depois do;.
fn main() { // O iterador é dono do Vec, então nada pega um temporário emprestado let mut doubled = vec![1, 2, 3].into_iter().map(|n| n * 10); println!("{:?}", doubled.next()); println!("{:?}", doubled.next());
// .copied() transforma Option<&i32> em Option<i32>, um valor sem empréstimo let best = vec![72, 95, 88].iter().max().copied(); println!("Melhor: {best:?}");}Saída:
Some(10)Some(20)Melhor: Some(95)Se você precisar do Vec de novo mais tarde, volte para a Correção 1: let scores = vec![72, 95, 88]; e depois scores.iter().max(). Veja Iteradores.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Travando um Mutex no meio de uma cadeia. let first = names.lock().unwrap().first(); dá E0716, porque o guard da trava é um temporário. Quando ele é descartado, a trava é liberada, então first não pode continuar apontando para dentro dela. Correção: let guard = names.lock().unwrap(); e depois guard.first(), ou copie os dados para fora de uma vez com .first().cloned(). Veja Estado compartilhado.
Passando &temporário para uma função que devolve uma referência. Com fn shout(s: &str) -> &str, a linha let t = shout(&make_title()); dá E0716: o &str devolvido pega emprestada a String temporária, e a extensão de lifetime não atravessa uma chamada de função. Correção: let title = make_title(); e depois shout(&title).
Devolvendo de uma função. Se você escreve make_title().as_str() como a última linha de uma função que devolve uma referência (como -> &'static str), recebe o E0515 (“cannot return value referencing temporary value”, não é possível devolver um valor que referencia um valor temporário) no lugar. Correção: devolva a String.
Erros relacionados
Seção intitulada “Erros relacionados”- E0597: borrowed value does not live long enough (o valor emprestado não vive o suficiente): o mesmo problema para um valor que tem nome e é descartado em um
}. - E0515: cannot return reference to local variable: devolver, de uma função, um empréstimo de um valor local ou temporário.
- E0106: missing lifetime specifier (falta o especificador de lifetime): um campo de struct como
text: &strprecisa de um lifetime. Muitas vezes é sinal de queStringé a melhor escolha. - E0502: mutable borrow while immutably borrowed: outro erro de “um empréstimo ainda está em uso”, só que sobre mudar dados em vez de descartá-los.
- E0382: borrow of moved value (uso de valor movido): depois de trocar para valores próprios, cuidado para não usar um valor depois de movê-lo.
- Todos os erros do compilador explicados
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. No Rust, o que é um "temporário"?
2. let s = String::from("hi").as_str(); seguido de println!("{s}"); dá E0716. Qual é a correção mais comum?
3. Qual linha compila graças à extensão do lifetime de temporários?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0716 — Rust error codes index (em inglês): a explicação oficial deste erro, com a correção usando
let. - References and Borrowing — The Rust Book (em inglês): as regras de borrowing, incluindo por que as referências nunca podem ficar pendentes.
- Temporary scopes — The Rust Reference (em inglês): as regras exatas de quando um temporário é descartado.
- Temporary lifetime extension — The Rust Reference (em inglês): quais formas de
letmantêm um temporário vivo, com exemplos. - Validating References with Lifetimes — The Rust Book (em inglês): como o Rust compara quanto tempo um valor vive com quanto tempo uma referência é usada.