Erro E0106 do Rust: missing lifetime specifier (falta o lifetime)
O erro E0106 quer dizer: você escreveu uma referência (&) no tipo de retorno de uma função ou em uma struct, e o Rust não consegue saber de onde vem esse empréstimo. Não é um problema na sua lógica. O compilador só precisa de mais uma palavra sua, ou de uma pequena mudança de plano. Quase todo mundo encontra esse erro na primeira semana de Rust, e a correção costuma ser uma única linha. (Esta página faz parte de Erros do compilador Rust explicados.)
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0106]: missing lifetime specifier --> src/main.rs:1:32 |1 | fn longer(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 longer<'a>(a: &'a str, b: &'a str) -> &'a str { | ++++ ++ ++ ++Em português: “falta um especificador de lifetime”. A ajuda diz que o tipo de retorno contém um valor emprestado, mas a assinatura não diz se ele vem de a ou de b, e sugere adicionar um lifetime com nome.
O programa que causa o erro:
fn longer(a: &str, b: &str) -> &str { if a.len() >= b.len() { a } else { b }}
fn main() { println!("{}", longer("apple", "fig"));}O que significa
Seção intitulada “O que significa”Uma referência é um empréstimo: ela aponta para dados que pertencem a outra pessoa. Sempre que uma função retorna uma referência, o Rust precisa saber de quem são os dados para onde ela aponta, para conferir se esses dados continuam vivos enquanto quem chamou usa o resultado.
Olhe as três marcas embaixo da linha 1. O compilador sublinha a e b com ---- e o tipo de retorno com ^. Ele está dizendo: “você retorna um &str emprestado, e ele pode vir de a ou de b. De qual?” Ele não vai chutar.
Pense em um livro de biblioteca. Se um amigo te entrega um livro e diz “é emprestado”, você quer saber de quem, para saber quando ele precisa voltar. Um lifetime (tempo de vida) é essa etiqueta no empréstimo.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”O Rust garante que uma referência nunca vive mais do que os dados para onde ela aponta (nada de “ponteiros pendurados”). Para conferir isso em uma chamada de função, o compilador olha só a assinatura da função, não o corpo. Então a própria assinatura precisa dizer de onde vem a referência retornada.
Na maior parte do tempo você nunca escreve lifetimes, porque o Rust os preenche sozinho com três regras de elisão simples:
- Cada parâmetro que é uma referência ganha o seu próprio lifetime.
- Se existe exatamente um parâmetro que é referência, a referência retornada é emprestada dele.
- Se for um método com
&selfou&mut self, a referência retornada é emprestada deself.
O E0106 aparece quando nenhuma dessas regras dá uma resposta: dois ou mais parâmetros que são referências (e nenhum self), nenhum parâmetro que seja referência, ou uma referência guardada em uma struct. A lição de Lifetimes aprofunda isso, e a de Borrowing explica as próprias referências.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Adicione um lifetime com nome ('a)
Seção intitulada “Correção 1: Adicione um lifetime com nome ('a)”Escolha esta quando a função realmente retorna um pedaço de uma das entradas. 'a é só um nome. Escrevê-lo nas entradas e na saída diz “o resultado vive tanto quanto essas entradas”:
// 'a: o &str retornado é emprestado de a ou de b, então vive tanto quanto os dois.fn longer<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() >= b.len() { a } else { b }}
fn main() { let first = String::from("apple"); let second = String::from("fig"); println!("Palavra mais longa: {}", longer(&first, &second));}Saída:
Palavra mais longa: apple(As palavras ficam em inglês de propósito: a função compara o tamanho em bytes, e letras com acento ocupam 2 bytes.)
Se o resultado sempre vem de um só parâmetro, coloque 'a só nesse parâmetro: fn lookup<'a>(map: &'a HashMap<String, String>, key: &str) -> Option<&'a str>. A sugestão do compilador coloca 'a em tudo, o que funciona, mas é mais restritivo do que o necessário.
Correção 2: Retorne uma String própria
Seção intitulada “Correção 2: Retorne uma String própria”Escolha esta quando a função monta um texto novo, ou quando você simplesmente não quer prender quem chamou a um empréstimo. Uma String própria pertence a quem chamou, então não há nada para rastrear:
fn greeting(name: &str, lang: &str) -> String { if lang == "es" { format!("Hola, {name}!") } else { format!("Hello, {name}!") }}
fn main() { println!("{}", greeting("Ana", "es")); println!("{}", greeting("Sam", "en"));}Saída:
Hola, Ana!Hello, Sam!Isso custa uma pequena alocação de memória. Para a maioria dos programas, tudo bem. Não se sinta mal por isso.
Correção 3: Guarde dados próprios na sua struct
Seção intitulada “Correção 3: Guarde dados próprios na sua struct”Um campo de struct como title: &str dá o mesmo erro:
error[E0106]: missing lifetime specifier --> src/main.rs:2:12 |2 | title: &str, | ^ expected named lifetime parameterO compilador espera um lifetime com nome no campo title.
Para structs, a melhor correção quase sempre é ser dono dos dados: use String em vez de &str, e Vec<T> em vez de &[T]. Assim a struct pode viver o quanto você quiser:
#[derive(Debug)]struct Book { title: String, // própria, não &str pages: u32,}
fn main() { let book = Book { title: String::from("Dune"), pages: 412 }; println!("{} ({} páginas)", book.title, book.pages);}Saída:
Dune (412 páginas)Só dê um lifetime a uma struct quando ela for uma visão de curta duração de dados que vivem em outro lugar, como um parser olhando um texto grande. Aí você escreve <'a> na struct e no campo:
struct Excerpt<'a> { text: &'a str, // emprestado do romance abaixo}
fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let first = novel.split('.').next().unwrap(); let excerpt = Excerpt { text: first }; println!("Trecho: {}", excerpt.text);}Saída:
Trecho: Call me IshmaelCorreção 4: Use 'static para literais de string
Seção intitulada “Correção 4: Use 'static para literais de string”Quando uma função não tem nenhum parâmetro que seja referência, o erro diz que “não há nenhum valor de onde emprestar”:
error[E0106]: missing lifetime specifier --> src/main.rs:1:22 |1 | fn default_name() -> &str { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed fromSe você só retorna literais de string como "OK", escreva &'static str. Os literais ficam gravados dentro do seu programa, então vivem durante toda a execução:
fn status_text(code: u16) -> &'static str { match code { 200 => "OK", 404 => "Not Found", _ => "Unknown", }}
fn main() { println!("200 significa {}", status_text(200)); println!("404 significa {}", status_text(404));}Saída:
200 significa OK404 significa Not FoundUse 'static só para literais e constantes de verdade. Se o texto é montado durante a execução (com format!, ou lido de um arquivo), 'static não ajuda: retorne uma String (Correção 2).
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”- Funções auxiliares com duas entradas, como
fn lookup(map: &HashMap<String, String>, key: &str) -> Option<&str>. Correção: ligue a saída só amapcom'a(Correção 1). - Structs que guardam texto, como um
User { name: &str }montado a partir de uma entrada lida. Correção: use camposString(Correção 3). - Funções que retornam mensagens fixas, como
fn error_label() -> &str. Correção:&'static str(Correção 4).
Os métodos são a boa notícia: fn name(&self) -> &str não precisa de lifetime nenhum, graças à regra 3.
Erros relacionados
Seção intitulada “Erros relacionados”- E0382: borrow of moved value: outro erro de ownership (posse), sobre usar um valor depois de entregá-lo.
- E0502: mutable borrow while immutably borrowed: o borrow checker trabalhando depois que seus lifetimes estão no lugar.
- E0499: two mutable borrows: dois
&mutpara os mesmos dados ao mesmo tempo. - E0308: mismatched types: por exemplo, retornar
Stringde uma função que diz&str. E0515: cannot return reference to local variable: você tentou a Correção 1 com dados criados dentro da função. Retorne umaStringprópria (Correção 2).- Todos os erros do compilador explicados
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Por que fn first_word(s: &str) -> &str compila sem nenhum lifetime?
2. Sua struct Config { path: &str } dá E0106. Qual costuma ser a melhor correção?
3. Uma função monta uma mensagem com format! e a retorna. Qual deve ser o tipo de retorno?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0106 — Rust error codes index (em inglês): a explicação oficial deste erro.
- Validating References with Lifetimes — The Rust Book (em inglês): a história completa dos lifetimes, com o exemplo
longeste structs que guardam referências. - Lifetime elision — The Rust Reference (em inglês): as regras exatas que o Rust usa para preencher os lifetimes por você.
- Elision — Rust by Example (em inglês): exemplos curtos de quando você pode omitir os lifetimes.
- Lifetimes — Rust by Example (em inglês): exemplos executáveis de lifetimes explícitos em funções, métodos e structs.