E0106: Lifetime Ausente
O erro E0106 ocorre quando o compilador não consegue determinar automaticamente o lifetime (tempo de vida) de uma referência. Isso geralmente acontece em assinaturas de funções e definições de structs onde há ambiguidade sobre quanto tempo uma referência deve ser válida.
A Mensagem de Erro
error[E0106]: missing lifetime specifier
--> src/main.rs:1:24
|
1 | fn maior(a: &str, b: &str) -> &str {
| ---- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature
does not say whether it is borrowed from `a` or `b`
help: consider introducing a named lifetime parameter
|
1 | fn maior<'a>(a: &'a str, b: &'a str) -> &'a str {
| ++++ ++ ++ ++
O Que Significa
Todo valor referenciado em Rust tem um lifetime — o período durante o qual a referência é válida. Na maioria dos casos, o compilador infere os lifetimes automaticamente através de regras chamadas lifetime elision rules (regras de elisão).
Porém, quando uma função retorna uma referência e recebe múltiplas referências como parâmetros, o compilador não sabe qual referência de entrada está relacionada à de saída. Ele precisa que você explicite essa relação com anotações de lifetime.
As três regras de elisão são:
- Cada parâmetro de referência recebe seu próprio lifetime
- Se há exatamente um parâmetro de referência, seu lifetime é atribuído a todas as referências de saída
- Se um dos parâmetros é
&selfou&mut self, seu lifetime é atribuído às referências de saída
Quando nenhuma regra se aplica, você precisa anotar manualmente.
Código com Erro
Função com dois parâmetros de referência retornando referência:
// ERRO: o compilador não sabe se o retorno vem de `a` ou `b`
fn maior(a: &str, b: &str) -> &str {
if a.len() > b.len() {
a
} else {
b
}
}
Struct contendo referência:
// ERRO: struct com referência precisa de lifetime explícito
struct Trecho {
conteudo: &str,
}
Como Resolver
Solução 1: Adicionar Anotação de Lifetime
Declare um parâmetro de lifetime genérico (convencionalmente 'a) e aplique-o:
fn maior<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() > b.len() {
a
} else {
b
}
}
fn main() {
let resultado;
let string1 = String::from("texto longo");
{
let string2 = String::from("xyz");
resultado = maior(&string1, &string2);
println!("Maior: {}", resultado);
}
}
O 'a diz ao compilador: “o retorno vive pelo menos tanto quanto o menor dos lifetimes de a e b”.
Para structs:
struct Trecho<'a> {
conteudo: &'a str,
}
fn main() {
let texto = String::from("Olá, Rust Brasil!");
let trecho = Trecho {
conteudo: &texto[0..3],
};
println!("Trecho: {}", trecho.conteudo);
}
Solução 2: Usar 'static Quando Apropriado
Se a referência aponta para dados que vivem durante toda a execução do programa (como string literals), use 'static:
fn saudacao() -> &'static str {
"Olá, mundo!"
}
fn main() {
println!("{}", saudacao());
}
Cuidado: Não use 'static como solução universal. Ele é apropriado apenas para dados realmente estáticos. Forçar 'static em outros cenários levará a mais erros.
Solução 3: Retornar Tipo Owned em Vez de Referência
Às vezes, a solução mais simples é retornar um tipo com ownership em vez de uma referência:
fn maior(a: &str, b: &str) -> String {
if a.len() > b.len() {
a.to_string()
} else {
b.to_string()
}
}
fn main() {
let resultado = maior("Rust", "Go");
println!("Maior: {}", resultado);
}
Essa abordagem tem custo de alocação, mas elimina completamente a complexidade de lifetimes. Para muitos casos, especialmente quando a string precisa ser modificada ou armazenada, é a solução mais prática.
Lifetimes em Implementações
Quando sua struct tem lifetime, as implementações também precisam declará-lo:
struct Trecho<'a> {
conteudo: &'a str,
}
impl<'a> Trecho<'a> {
fn novo(conteudo: &'a str) -> Self {
Trecho { conteudo }
}
fn tamanho(&self) -> usize {
self.conteudo.len()
}
}
Dica Prática
Se lifetimes estão deixando seu código muito complexo, considere se você realmente precisa de referências. Em muitos casos, usar tipos com ownership (String em vez de &str, Vec<T> em vez de &[T]) simplifica significativamente o código com custo mínimo de performance.