# Lifetimes em Rust Explicados: Anotações, Elisão e Perguntas de Entrevista 2026 > Dominar os lifetimes do Rust: anotações explícitas, regras de elisão e preparação para entrevistas técnicas com exemplos práticos. - Published: 2026-08-27 - Updated: 2026-08-27 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- Os lifetimes em Rust constituem o mecanismo do compilador para rastrear quanto tempo as referências permanecem válidas. Diferente de linguagens com garbage collection onde o gerenciamento de memória acontece em tempo de execução, o borrow checker do Rust valida a validade das referências em tempo de compilação, eliminando categorias inteiras de bugs de memória antes mesmo do código ser executado. > **Resposta Rápida para Entrevistas** > > Um lifetime em Rust é uma construção em tempo de compilação que descreve o escopo durante o qual uma referência permanece válida. O borrow checker usa lifetimes para garantir que referências nunca sobrevivam aos dados que apontam, prevenindo ponteiros pendentes sem overhead em tempo de execução. ## O que os Lifetimes Realmente Representam na Memória Lifetimes não se tratam de quanto tempo os valores existem. Eles descrevem por quanto tempo as *referências* a esses valores permanecem válidas. Cada referência em Rust possui um lifetime, mesmo quando as anotações são omitidas. Considere esta função que o compilador rejeita: ```rust // dangling_reference.rs fn create_dangling() -> &String { let s = String::from("hello"); &s // ERRO: `s` é liberada no final da função } ``` A String `s` existe apenas dentro do escopo da função. Retornar uma referência a ela criaria um ponteiro pendente, já que a memória da String é desalocada quando a função retorna. O borrow checker detecta isso em tempo de compilação. A solução requer retornar um valor próprio ou garantir que os dados referenciados sobrevivam à chamada da função: ```rust // valid_return.rs // Opção 1: Retornar valor próprio fn create_owned() -> String { String::from("hello") } // Opção 2: Referenciar dados que sobrevivem à função fn first_word(s: &str) -> &str { s.split_whitespace().next().unwrap_or("") } ``` Em `first_word`, o `&str` retornado empresta da entrada `s`, então permanece válido enquanto `s` estiver. O chamador controla o lifetime da entrada. ## Sintaxe e Semântica das Anotações de Lifetime As anotações de lifetime explícitas usam a sintaxe `'a`, `'b`, e assim por diante. Estas não são instruções ao compilador sobre quanto tempo as referências devem viver. Elas descrevem relações entre os lifetimes de múltiplas referências. A [Referência do Rust](https://doc.rust-lang.org/reference/trait-bounds.html#lifetime-bounds) define anotações de lifetime como parâmetros genéricos que restringem quanto tempo as referências devem permanecer válidas entre si. ```rust // lifetime_annotations.rs // Ambas as entradas e a saída compartilham o mesmo lifetime 'a fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let string1 = String::from("long string"); let result; { let string2 = String::from("short"); result = longest(&string1, &string2); println!("A mais longa: {}", result); // Válido: ambas strings existem } // println!("{}", result); // ERRO: string2 liberada } ``` A anotação `'a` informa ao compilador: a referência retornada será válida pela *interseção* dos lifetimes de `x` e `y`. Como `string2` tem um lifetime mais curto, `result` não pode ser usado após `string2` ser liberada. Múltiplos lifetimes distintos expressam relações mais complexas: ```rust // multiple_lifetimes.rs // A saída está ligada apenas ao lifetime do primeiro parâmetro fn first_only<'a, 'b>(x: &'a str, _y: &'b str) -> &'a str { x } fn main() { let owned = String::from("owned"); let result; { let temporary = String::from("temporary"); result = first_only(&owned, &temporary); } // result ainda válido: depende apenas de owned println!("{}", result); } ``` ## Regras de Elisão de Lifetimes no Rust 2024 O compilador aplica três regras de elisão para inferir lifetimes quando as anotações são omitidas. Essas regras, documentadas no [Rustonomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html), reduzem código repetitivo sem sacrificar a segurança. > **As Três Regras de Elisão** > > 1. Cada referência de entrada recebe seu próprio parâmetro de lifetime > 2. Se existir exatamente um lifetime de entrada, ele se aplica a todas as referências de saída > 3. Se `&self` ou `&mut self` existir, seu lifetime se aplica a todas as referências de saída Essas regras lidam com a maioria dos padrões comuns: ```rust // elision_examples.rs // Regra 1: Cada entrada recebe seu próprio lifetime fn takes_two(x: &str, y: &str) {} // O compilador lê: fn takes_two<'a, 'b>(x: &'a str, y: &'b str) {} // Regra 2: Um único lifetime de entrada se propaga para a saída fn first_char(s: &str) -> &str { &s[0..1] } // O compilador lê: fn first_char<'a>(s: &'a str) -> &'a str // Regra 3: O lifetime de &self se propaga para a saída impl Parser { fn peek(&self) -> &Token { &self.tokens[self.position] } // O compilador lê: fn peek<'a>(&'a self) -> &'a Token } ``` Quando a elisão falha, o compilador requer anotações explícitas. Isso acontece mais frequentemente com funções que retornam referências derivadas de múltiplas entradas: ```rust // elision_fails.rs // ERRO: Não é possível determinar o lifetime de saída fn ambiguous(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } } // CORREÇÃO: A anotação explícita resolve a ambiguidade fn unambiguous<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } ``` ## Lifetimes em Structs e a Relação Outlives Structs que contêm referências requerem anotações de lifetime para expressar que os dados emprestados devem sobreviver à struct: ```rust // struct_lifetimes.rs struct Excerpt<'a> { text: &'a str, } impl<'a> Excerpt<'a> { // new empresta da entrada, então Excerpt não pode sobreviver à fonte fn new(source: &'a str, start: usize, end: usize) -> Self { Excerpt { text: &source[start..end] } } // level() retorna dados próprios, sem lifetime na assinatura fn level(&self) -> u32 { self.text.len() as u32 / 10 } } fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let excerpt = Excerpt::new(&novel, 0, 16); println!("Trecho: {}", excerpt.text); } // novel liberada, mas excerpt já está fora de escopo ``` O `'a` em `Excerpt<'a>` vincula a validade da struct ao lifetime do `text` emprestado. Tentar usar um `Excerpt` após sua String fonte ser liberada gera um erro de compilação. Para structs com múltiplas referências, cada uma pode ter lifetimes distintos: ```rust // multiple_struct_lifetimes.rs struct Comparison<'a, 'b> { baseline: &'a str, candidate: &'b str, } impl<'a, 'b> Comparison<'a, 'b> { fn new(baseline: &'a str, candidate: &'b str) -> Self { Comparison { baseline, candidate } } fn baseline_only(&self) -> &'a str { self.baseline } } ``` ## O Lifetime 'static e Quando Usá-lo O lifetime `'static` indica dados que vivem durante toda a execução do programa. Literais de string têm esse lifetime porque estão embutidos no binário: ```rust // static_lifetime.rs let s: &'static str = "Eu vivo para sempre"; // Constantes são implicitamente 'static const CONFIG_VERSION: &str = "2.0.0"; // Globais thread-safe requerem 'static static COUNTER: AtomicU64 = AtomicU64::new(0); ``` Um padrão comum envolve os bounds `T: 'static`, o que frequentemente confunde desenvolvedores. Esse bound significa que `T` não contém referências não estáticas, não que `T` deve ser uma referência: ```rust // static_bound.rs use std::thread; fn spawn_task(data: T) { thread::spawn(move || { // data movida para a thread, deve viver independentemente println!("Processando na thread"); }); } fn main() { let owned = String::from("dados próprios"); spawn_task(owned); // OK: String é 'static (possui seus dados) let reference = "emprestada"; // spawn_task(reference); // OK: &'static str } ``` A thread pode sobreviver à função que a criou, então dados movidos para threads não devem conter referências a variáveis locais da pilha. ## Erros de Lifetime Comuns e Suas Soluções Vários padrões fazem os desenvolvedores tropeçarem consistentemente. Entender estes acelera a depuração. **Retornar referências a variáveis locais:** ```rust // error_local_ref.rs fn bad_split(text: &str, delimiter: char) -> (&str, &str) { let parts: Vec<&str> = text.split(delimiter).collect(); (parts[0], parts[1]) // OK: parts[i] empresta de text, não do Vec } fn truly_bad() -> &str { let local = String::from("local"); &local // ERRO: local é liberada no final da função } ``` **Armazenar referências em structs com lifetimes incompatíveis:** ```rust // error_struct_lifetime.rs struct Cache { data: String, // view: &str, // ERRO: precisa de parâmetro de lifetime } // Structs auto-referenciais requerem unsafe ou crates como ouroboros struct CacheWithView<'a> { data: String, view: Option<&'a str>, // Não pode apontar para self.data com segurança } ``` Structs auto-referenciais, onde um campo referencia outro campo, requerem `Pin` com código unsafe ou crates como [ouroboros](https://docs.rs/ouroboros/). O módulo [Rust smart pointers](/technologies/rust/interview-questions/smart-pointers) cobre esses padrões avançados. **Bounds de lifetime em implementações de traits:** ```rust // trait_lifetime_bounds.rs trait Processor { fn process<'a>(&self, input: &'a str) -> &'a str; } struct Prefixer { prefix: String, } impl Processor for Prefixer { // Não é possível retornar &format!(...) - seria temporário fn process<'a>(&self, input: &'a str) -> &'a str { input // Deve retornar input ou parte dele } } ``` ## Higher-Ranked Trait Bounds (HRTBs) para Lifetimes Genéricos Higher-ranked trait bounds usam a sintaxe `for<'a>` para expressar que um tipo deve satisfazer um trait para *qualquer* lifetime, não apenas um específico: ```rust // hrtb.rs use std::fmt::Debug; // F deve ser chamável com qualquer lifetime 'a fn apply_to_refs(f: F) where F: for<'a> Fn(&'a str) -> &'a str, { let owned = String::from("test"); let result = f(&owned); println!("{}", result); } fn identity(s: &str) -> &str { s } fn main() { apply_to_refs(identity); } ``` HRTBs aparecem frequentemente em APIs que aceitam closures e no ecossistema [Rust async/await](/blog/rust/rust-async-await-tokio-futures-concurrency) onde futures devem funcionar com referências de lifetimes variados. ## Perguntas de Entrevista sobre Lifetimes em Rust Entrevistas técnicas investigam a compreensão de lifetimes em múltiplos níveis de profundidade. Essas perguntas aparecem regularmente em posições de Rust. **Pergunta: Por que este código falha ao compilar?** ```rust // interview_q1.rs fn get_str() -> &str { "hello" } ``` **Resposta:** O tipo de retorno precisa de uma anotação de lifetime explícita. Embora o literal de string tenha lifetime `'static`, a assinatura da função não expressa isso. A correção: `fn get_str() -> &'static str`. **Pergunta: Explicar por que isso compila e se é seguro:** ```rust // interview_q2.rs fn longest<'a>(x: &'a str, _y: &str) -> &'a str { x } ``` **Resposta:** Isso compila porque o lifetime de saída está ligado apenas a `x`. O parâmetro `_y` pode ter qualquer lifetime já que o valor de retorno não depende dele. É seguro: a validade da referência retornada depende apenas do lifetime de `x`. **Pergunta: O que acontece quando se tenta armazenar uma referência junto com dados próprios?** ```rust // interview_q3.rs struct Config<'a> { name: String, description: &'a str, } ``` **Resposta:** Esse padrão é válido mas restringe como `Config` pode ser usado. A struct não pode sobreviver ao que `description` referencia. Para dados próprios que precisam referenciar a si mesmos, considere usar `String` para ambos os campos, ou as técnicas cobertas em [Rust ownership e borrowing](/blog/rust/rust-ownership-borrowing-demystified). **Pergunta: Como os lifetimes interagem com trait objects?** ```rust // interview_q4.rs trait Formatter { fn format(&self, input: &str) -> String; } fn get_formatter<'a>() -> Box { // ... } ``` **Resposta:** Trait objects têm um bound de lifetime `'static` implícito por padrão. Escrever `Box` permite explicitamente que o trait object contenha referências com lifetime `'a`. Sem o bound explícito, `Box` equivale a `Box`. ## Variância de Lifetimes: Covariância e Contravariância Variância determina como lifetimes se relacionam quando tipos estão aninhados. Referências do Rust seguem estas regras: - `&'a T` é **covariante** em `'a`: um lifetime mais longo pode substituir um mais curto - `&'a mut T` é **invariante** em `T`: o tipo deve corresponder exatamente - `fn(&'a T)` é **contravariante** em `'a`: um lifetime mais curto pode substituir um mais longo ```rust // variance.rs fn covariant_example() { let s: &'static str = "static"; let r: &str = s; // OK: 'static vive mais que qualquer 'a } fn invariant_example() { let mut vec: Vec<&'static str> = vec!["a"]; // let s = String::from("local"); // vec.push(&s); // ERRO: &s não é &'static str } ``` Entender variância importa ao projetar APIs genéricas que aceitam ou retornam referências. O artigo [Rust traits e generics](/blog/rust/rust-traits-generics-advanced-guide) cobre padrões genéricos avançados. ## Anotações de Lifetime na Prática: Pontos Chave - Lifetimes descrevem validade de referências, não existência de valores. O borrow checker os usa para prevenir ponteiros pendentes em tempo de compilação. - Regras de elisão lidam com a maioria dos casos automaticamente. Anotações explícitas se tornam necessárias ao retornar referências derivadas de múltiplas entradas. - Lifetimes de struct expressam a relação outlives: qualquer struct contendo referências deve ser parametrizada pelos lifetimes dessas referências. - O bound `'static` em genéricos significa "não contém referências não estáticas", não "deve ser uma referência". - Structs auto-referenciais requerem tratamento especial através de `Pin`, código unsafe, ou crates auxiliares. - Perguntas de entrevista focam em entender por que o código falha ao compilar e como anotações de lifetime mudam as garantias de validade. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pt/blog/rust/rust-lifetimes-explained-annotations-elision-interview