Pular para o conteúdo

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.)

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 function

Em 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 variable quando você escreve &greeting.
  • cannot return value referencing local variable quando você chama um método que pega emprestado, como greeting.as_str() ou &scores[2..].
  • cannot return value referencing temporary value quando os dados nunca foram guardados em uma variável, como text.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).

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.

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).

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]
▶ Experimente você mesmo

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]
▶ Experimente você mesmo

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: OK
404: Not Found
418: Unknown
▶ Experimente você mesmo

Nã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)
▶ Experimente você mesmo

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.

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.

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