Erro E0515 do Rust: cannot return reference to local variable
O E0515 quer dizer: sua função tenta retornar uma referência (&) para algo que ela mesma criou. Tudo o que uma função cria é liberado quando ela retorna, então a referência apontaria para o nada. Quase todo iniciante em Rust esbarra nesse erro, e a correção costuma ser tirar um único & e retornar o próprio valor. (Esta página faz parte de Erros do compilador Rust explicados.)
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0515]: cannot return reference to local variable `greeting` --> src/main.rs:3:5 |3 | &greeting // ❌ greeting é liberada quando retornamos | ^^^^^^^^^ returns a reference to data owned by the current functionEm português: “não é possível retornar uma referência para a variável local greeting”. O rótulo explica que você está retornando uma referência para dados que pertencem à função atual.
Este é o programa que causa o erro:
fn make_greeting(name: &str) -> &str { let greeting = format!("Hello, {name}!"); // uma String nova, dona: esta função &greeting // ❌ greeting é liberada quando retornamos}
fn main() { let text = make_greeting("Ferris"); println!("{text}");}Você vai ver algumas versões do mesmo erro. Todas querem dizer a mesma coisa:
cannot return reference to local variablequando você escreve&greeting.cannot return value referencing local variablequando você chama um método que pega emprestado, comogreeting.as_str()ou&scores[2..].cannot return value referencing temporary valuequando os dados nunca foram guardados em uma variável, comotext.to_uppercase().as_str().
Toda versão traz o rótulo “returns a value referencing data owned by the current function” (retorna um valor que referencia dados da função atual).
O que significa
Seção intitulada “O que significa”greeting é uma variável local: ela é criada dentro de make_greeting e pertence a essa função. Quando a função retorna, todas as variáveis locais são liberadas (o Rust faz o drop delas). O &str que você retorna apontaria para dentro de uma String que não existe mais.
É como se mudar de um apartamento e entregar a chave para um amigo. A chave é de verdade, mas o apartamento agora está vazio, ou outra pessoa mora lá. Seu amigo precisa dos móveis, não da chave.
Um caminho comum até aqui: primeiro você escreveu greeting (uma String) em uma função que retorna &str, recebeu o E0308 (mismatched types, tipos incompatíveis), e o compilador sugeriu “consider borrowing here: &greeting”. Essa sugestão acerta o tipo, mas leva direto ao E0515. A correção de verdade é mudar o tipo de retorno.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Uma referência nunca pode viver mais do que o valor para onde ela aponta. Retornar uma referência para uma variável local criaria uma referência pendurada (dangling reference), um ponteiro para memória já liberada. Em C e C++ isso compila e depois trava (ou pior, retorna lixo sem avisar). O Rust pega o problema na hora da compilação.
Uma referência retornada precisa ser emprestada de algo que vive fora da função: um parâmetro que quem chamou passou, um campo de self, ou um valor 'static, como um literal de string. As variáveis locais da própria função não servem. Por isso as anotações de lifetime não corrigem o E0515: 'a só descreve quanto tempo os dados vivem, não faz uma variável local viver mais. Veja Ownership (posse) e Borrowing (empréstimo).
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Retorne o valor próprio
Seção intitulada “Correção 1: Retorne o valor próprio”Escolha esta quando a função cria os dados. Na maioria das vezes, esta é a resposta certa. Retorne String em vez de &str (ou Vec<T> em vez de &[T]) e tire o &. O valor é movido para quem chamou, que passa a ser o dono:
fn make_greeting(name: &str) -> String { let greeting = format!("Olá, {name}!"); greeting // ✅ entrega a String inteira para quem chamou}
fn top_scores() -> Vec<u32> { let mut scores = vec![42, 97, 63, 88]; scores.sort(); scores.split_off(2) // as duas maiores, em um Vec novo}
fn main() { let text = make_greeting("Ferris"); println!("{text}"); println!("{:?}", top_scores());}Saída:
Olá, Ferris![88, 97]Não se preocupe com o custo: retornar uma String ou um Vec move só um ponteiro pequeno, o tamanho e a capacidade. O texto em si não é copiado. Você também poderia escrever simplesmente format!("Olá, {name}!") como última linha.
Correção 2: Receba os dados como parâmetro e retorne uma referência para eles
Seção intitulada “Correção 2: Receba os dados como parâmetro e retorne uma referência para eles”Escolha esta quando quem chama já é dono dos dados e você só quer apontar para uma parte deles. Se a função trabalha com um parâmetro emprestado, a referência retornada é emprestada desse parâmetro, que vive fora da função:
fn top_two(scores: &mut [u32]) -> &[u32] { scores.sort(); &scores[scores.len() - 2..] // ✅ aponta para os dados de quem chamou}
fn main() { let mut scores = vec![42, 97, 63, 88]; // dono: main let best = top_two(&mut scores); println!("{best:?}");}Saída:
[88, 97]Com um único parâmetro que é referência, o Rust preenche o lifetime para você. Com dois ou mais, talvez você precise de 'a (veja o E0106). Isso só funciona se você retorna um pedaço da entrada. Um valor novo montado a partir da entrada (to_uppercase(), format!, trim().to_string()) é uma variável local de novo, então volte para a Correção 1. Veja Slices.
Correção 3: Retorne &'static str para literais de string
Seção intitulada “Correção 3: Retorne &'static str para literais de string”Escolha esta quando todo resultado possível é um texto fixo escrito no seu código. Os literais de string ficam guardados dentro do próprio programa e vivem para sempre, então retornar uma referência para um deles é sempre seguro:
fn status_text(code: u16) -> &'static str { match code { 200 => "OK", 404 => "Not Found", 500 => "Internal Server Error", _ => "Unknown", }}
fn main() { for code in [200, 404, 418] { println!("{code}: {}", status_text(code)); }}Saída:
200: OK404: Not Found418: UnknownNão tente “consertar” o resultado de um format! escrevendo &'static str na assinatura. Você só vai receber o E0515 de novo, porque a String continua sendo local.
Correção 4: Use Cow quando às vezes você empresta e às vezes cria
Seção intitulada “Correção 4: Use Cow quando às vezes você empresta e às vezes cria”Escolha esta quando a função normalmente retorna a entrada sem mudanças, mas às vezes precisa montar uma string nova. Cow (clone-on-write, “clona ao escrever”) guarda um &str emprestado ou uma String própria, então você só aloca memória quando precisa:
use std::borrow::Cow;
fn safe_file_name(name: &str) -> Cow<'_, str> { if name.contains(' ') { Cow::Owned(name.replace(' ', "_")) // String nova só quando precisa } else { Cow::Borrowed(name) // nenhuma cópia }}
fn main() { for name in ["report.txt", "my notes.txt"] { let clean = safe_file_name(name); let kind = match clean { Cow::Borrowed(_) => "emprestado", Cow::Owned(_) => "próprio", }; println!("{clean} ({kind})"); }}Saída:
report.txt (emprestado)my_notes.txt (próprio)Cow é uma otimização. Se você não tem certeza de que precisa dele, retorne uma String (Correção 1). Mais em Weak e Cow.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Retornar s.as_str() de uma String local. greeting.as_str() pega greeting emprestada, então falha do mesmo jeito que &greeting, com a mensagem “cannot return value referencing local variable”. Correção: retorne greeting e mude o tipo de retorno para String.
Um .map() que cria strings novas. Parece inofensivo, mas cada to_lowercase() cria uma String temporária que é liberada logo depois do .as_str():
fn tags(input: &str) -> Vec<&str> { input.split(',').map(|t| t.trim().to_lowercase().as_str()).collect() // ❌ cannot return value referencing temporary value}Correção: retorne Vec<String> e tire o .as_str(): .map(|t| t.trim().to_lowercase()).collect(). (Sem o to_lowercase(), t.trim() é uma slice de input, então Vec<&str> funcionaria. Veja Iteradores.)
Um método que formata um campo. fn full_name(&self) -> &str { &format!("{} {}", self.first, self.last) } dá “cannot return reference to temporary value”. Correção: -> String. Retornar &self.first não tem problema, porque esses dados vivem na struct, não no método. Veja Structs.
Uma função sem parâmetros que sejam referências. fn make_greeting() -> &String falha antes, com o E0106, porque não há nada de onde emprestar. A correção é a mesma: retorne String.
Erros relacionados
Seção intitulada “Erros relacionados”- E0106: missing lifetime specifier: muitas vezes aparece primeiro; adicionar
'anão corrige uma referência para uma variável local. - E0597: borrowed value does not live long enough: o mesmo problema dentro de uma função, com um bloco ou um laço em vez de um retorno.
- E0308: mismatched types: retornar
Stringonde foi declarado&str. Mude o tipo de retorno, não adicione&. - E0382: borrow of moved value: depois de passar a usar valores próprios, cuidado para não usar um valor depois de movê-lo.
E0716: temporary value dropped while borrowed: uma referência para um valor temporário que é usada mais adiante na mesma função.- Todos os erros do compilador explicados
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Uma função monta uma mensagem com format! e retorna &message. Qual costuma ser a correção certa?
2. Qual destas funções compila?
3. Quando Cow<str> é um bom tipo de retorno?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0515 — Rust error codes index (em inglês): a explicação oficial deste erro.
- Dangling References — The Rust Book (em inglês): o exemplo
danglee por que a resposta é retornar aStringdiretamente. - Validating References with Lifetimes — The Rust Book (em inglês): por que uma referência retornada precisa estar ligada a um dos parâmetros.
- The Slice Type — The Rust Book (em inglês): retornar slices
&stremprestadas da entrada, a versão segura da Correção 2. Cow— standard library docs (em inglês): o tipo clone-on-write da Correção 4, com exemplos.