Rust: Tempos de vida no Rust

Última atualização: 2026-08-26

O Lifetime é o mecanismo que o Rust utiliza para garantir que “as referências nunca fiquem pendentes” — ele atribui uma “data de validade” a cada referência.

Na verdade, você já está usando durações — todo o código das Lições 9 e 10 possui durações; o compilador simplesmente as “adivinhou” para você. Esta lição ensina quando é necessário especificar manualmente as durações.


1. O que você vai aprender


2. Diagramas conceituais

100%
flowchart LR
    subgraph "Function Signature fn longest<'a>"
        X["x: &'a str"] --> RET["Return Value: &'a str"]
        Y["y: &'a str"] --> RET
    end
    NOTE["'a = the shorter of x and y's lifetimes"] -.-> RET

3. Uma história sobre a caçada a um rato

(1) Frustração: O rato fugiu, mas eu continuo observando

Tom é um biólogo que está usando um rastreador para registrar a localização de ratos:

TEXT 📖 Somente leitura
The tracker locks onto the mouse A → Mouse A ran into the cave → The tracker is still pointing toward the entrance.

“Seria ótimo se o rastreador tivesse uma etiqueta que dissesse: ‘Válido até que o rato A saia do seu buraco’...”

(2) Soluções para o ciclo de vida do Rust

RUST
// Life Cycle Annotation 'a: Tell the compiler "the validity period of the return value does not exceed that of parameter x's validity period"
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 Strings");
    let result;

    {
        let string2 = String::from("short");
        result = longest(&string1, &string2);  // ✅ string2 and string1 are both still alive
        println!("The longer one is: {}", result);
    }  // string2 Destroyed here

    // println!("{}", result);  // ❌ If you print here, result references string2 which is destroyed
}  // string1 Destroyed here

Os períodos de validade são como etiquetas de “data de validade” anexadas aos rastreadores. O compilador usa essas etiquetas para determinar se uma referência ainda está dentro de seu período de validade. Neste exemplo, o período de validade de result não pode exceder o de string2.


4. Como funciona o ciclo de vida

(1) Toda referência tem um ciclo de vida

RUST
fn main() {
    let x: i32 = 10;           // x Its life cycle begins here
    let r: &i32 = &x;          // r Its life cycle begins here
    println!("r: {}", r);       // Usage r
}                               // r whose lifecycle ends first, then x ends

(2) Por que é necessário anotar os ciclos de vida?

Quando uma função retorna uma referência, o compilador precisa saber de qual parâmetro essa referência provém:

100%
graph TB
    A[Functions Return References] --> B{Which parameter does the return value point to?}
    B --> C[Pointer Parameters1 → The Lifecycle of Return Values ≤ Parameters1]
    B --> D[Pointer Parameters2 → The Lifecycle of Return Values ≤ Parameters2]
    B --> E[Point to one of the two → Must be labeled: Find the intersection]
    C --> F[No annotation required (the compiler can infer)]
    D --> F
    E --> G[Manual annotation is required 'a]
Cenário Exemplo É necessário fazer uma anotação?
Referência de entrada única fn first(x: &str) -> &str Não é necessário (regra de omissão)
Várias referências de entrada fn longest(x: &str, y: &str) -> &str Obrigatório (o compilador não sabe qual escolher)
Não retorna uma referência fn len(x: &str) -> usize Não é necessário
Estruturas que contêm referências struct S<'a> { r: &'a i32 } Obrigatório

(3) Referência rápida para locais de anotação do ciclo de vida

Localização Sintaxe Descrição
Definição da função fn foo<'a>(x: &'a str) -> &'a str Parâmetros e valores de retorno
Definição de estrutura struct S<'a> { r: &'a str } Uma estrutura não pode permanecer ativa após o fim de sua referência
bloco impl impl<'a> S<'a> Declarando o ciclo de vida ao implementar um método
restrição de característica where T: 'a Os tipos de restrição não podem conter referências de curta duração
Anotação estática &'static str Válida durante toda a execução do programa

5. Anotação do ciclo de vida

(1) Sintaxe

RUST
// 'a is the name of the lifecycle parameter (using 'a, 'b, 'c)
// Pronunciation: Regarding the lifecycle 'a, both x and y need to be alive for at least 'a

fn function<'a>(x: &'a str, y: &'a str) -> &'a str {
    // The lifecycle of return values = the shorter of x and y
}

(2) Regras de omissão do ciclo de vida

O compilador pode inferir automaticamente os tempos de vida (sem que você precise especificá-los) nos três casos a seguir:

Regra Significado
Cada referência de entrada tem seu próprio ciclo de vida fn foo(x: &str) atribui automaticamente um ciclo de vida a x
Apenas uma referência de entrada O tempo de vida da referência de saída = o tempo de vida da referência de entrada
Várias entradas, sendo que uma delas é &self ou &mut self O tempo de vida da referência de saída é &self
RUST
// No annotation required: there is only one input reference
fn first_word(s: &str) -> &str { &s[..] }

// Needs annotation: two input references
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

6. Exemplos de ciclo de vida

▶ Exemplo 1: Por que precisamos de um ciclo de vida? (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// What happens if there are no lifecycle annotations?
// ============================================

// This function has two input references, the return value could be any one of these
// ❌ If not annotated, a compilation error will occur: expected lifetime parameter
// fn longest_wrong(x: &str, y: &str) -> &str {
//     if x.len() > y.len() { x } else { y }
// }

// ✅ Correct Version: Lifecycle Annotation
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let s1 = String::from("hello");
    let result;

    {
        let s2 = String::from("hi");
        result = longest(&s1, &s2);  // result lifecycle = s2 lifecycle (the shorter one)
        println!("The longer one is: {}", result);
    }  // s2 destroyed, result invalid

    // println!("{}", result);  // ❌ The compiler will prevent: result's lifecycle has ended
}

Resultado:

TEXT 📖 Somente leitura
The longer one is: hello

O tempo de vida 'a é definido como o menor entre x e y. Se o tempo de vida do valor de retorno fosse definido como o maior, ele poderia ainda estar em uso depois que o menor já tivesse sido destruído — o que é exatamente o que o compilador está tentando evitar.


▶ Exemplo 2: Parâmetros para diferentes ciclos de vida (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// The two parameters have different lifecycles.
// ============================================

// Two Lifecycles: 'a corresponds to x, 'b corresponds to y
// The return value depends only on x, so use 'a
fn choose_first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
    x  // Return only x, so the lifecycle only needs 'a
}

fn main() {
    let x = String::from("Live to be a hundred years old");
    let result;
    {
        let y = String::from("Short-lived ghost");
        result = choose_first(&x, &y);  // ✅ result Depends solely on x
        println!("result: {}", result);
    }  // y destroyed, but result does not depend on y, so no problem

    println!("Remains valid even after leaving the inner layer: {}", result);  // ✅ x Still Alive
}

Resultado:

TEXT 📖 Somente leitura
result: Live to be a hundred years old
Remains valid even after leaving the inner layer: Live to be a hundred years old

Quando o valor de retorno depende exclusivamente de um determinado parâmetro, basta anotar o tempo de vida desse parâmetro. Mesmo que o parâmetro choose_first em y tenha vida curta, isso não afeta o resultado — pois o valor de retorno não utiliza absolutamente nenhum dado de y.


▶ Exemplo 3: Ciclo de vida em estruturas (Dificuldade ⭐⭐⭐⭐)

RUST
// ============================================
// Storing references in a structure -- the lifecycle must be specified.
// ============================================

struct Excerpt<'a> {
    content: &'a str,  // Structures Borrow External Strings
}

impl<'a> Excerpt<'a> {
    fn length(&self) -> usize {
        self.content.len()
    }

    fn announce(&self, announcement: &str) -> &str {
        println!("Announcement: {}", announcement);
        self.content  // Returning a Reference to the Interior of a Structure
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Can't find the period");

    let excerpt = Excerpt {
        content: &first_sentence,
    };

    println!("Excerpt: {}", excerpt.content);
    println!("Length: {}", excerpt.length());
    println!("Announcement of Results: {}", excerpt.announce("Welcome to this article"));
}  // excerpt must be destroyed before novel (content depends on novel)

Resultado:

TEXT 📖 Somente leitura
Excerpt: Call me Ishmael
Length: 15
Announcement of Results: Call me Ishmael

Quando uma estrutura contém referências, é necessário especificar um parâmetro de tempo de vida <'a> no nome da estrutura. Isso indica ao compilador que o tempo de vida de uma instância da estrutura não pode exceder o tempo de vida dos dados aos quais ela faz referência.


▶ Exemplo 4: Ciclo de vida “estático” (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// 'static: the reference remains valid throughout the program's execution.
// ============================================

fn main() {
    // A string literal is 'static
    let s: &'static str = "I exist throughout the entire lifecycle of a program";
    println!("{}", s);

    // 'static Constraints: T must not contain any non-static references
    fn print_static<T: 'static>(val: &T) {
        println!("{:?}", std::ptr::from_ref(val));
    }

    let num: i32 = 42;
    print_static(&num);  // ✅ i32 excludes references, complies with 'static

    // let msg = String::from("temp");
    // let r = &msg;
    // print_static(&r);  // ❌ r references non-'static msg
}

Resultado:

TEXT 📖 Somente leitura
I exist throughout the entire lifecycle of a program
0x...(Memory Address)

'static é a duração mais longa no Rust — os dados permanecem existentes desde o início do programa até o seu término. Literais de string têm uma duração 'static. Observação: 'static deve ser interpretado como “esta referência é válida para sempre”, e não como “esses dados sobrevivem até o fim”.


▶ Exemplo 5: Exercício abrangente — Analisador de texto (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Life Cycle in Practice: Zero-Copy Text Analysis
// ============================================

struct TextAnalyzer<'a> {
    text: &'a str,
}

impl<'a> TextAnalyzer<'a> {
    fn new(text: &'a str) -> Self {
        TextAnalyzer { text }
    }

    fn word_count(&self) -> usize {
        self.text.split_whitespace().count()
    }

    fn longest_word(&self) -> &'a str {
        self.text
            .split_whitespace()
            .max_by_key(|w| w.len())
            .unwrap_or("")
    }

    fn first_n_words(&self, n: usize) -> Vec<&'a str> {
        self.text.split_whitespace().take(n).collect()
    }

    fn line_count(&self) -> usize {
        self.text.lines().count()
    }

    fn char_count(&self) -> usize {
        self.text.chars().count()
    }

    fn summary(&self) -> String {
        format!(
            "Character: {}, Words: {}, Number of lines: {}, Longest Word: '{}'",
            self.char_count(),
            self.word_count(),
            self.line_count(),
            self.longest_word()
        )
    }
}

fn highlight_word<'a>(text: &'a str, word: &str) -> Vec<&'a str> {
    text.split_whitespace()
        .filter(|w| w.contains(word))
        .collect()
}

fn main() {
    let article = "Rust is a systems programming language \
that runs blazingly fast and prevents segfaults. \
Rust guarantees memory safety and thread safety.";

    let analyzer = TextAnalyzer::new(article);
    println!("=== Text Analysis ===");
    println!("{}", analyzer.summary());

    let top3 = analyzer.first_n_words(3);
    println!("\nFirst 3 words: {:?}", top3);

    let rust_mentions = highlight_word(article, "Rust");
    println!("Includes 'Rust' the word: {:?}", rust_mentions);

    let paragraph = "First line.\nSecond line.\nThird line.";
    let p_analyzer = TextAnalyzer::new(paragraph);
    println!("\nParagraph Analysis: {}", p_analyzer.summary());
}

Resultado:

TEXT 📖 Somente leitura
=== Text Analysis ===
Character: 124, Words: 18, Number of lines: 1, Longest Word: 'blazingly'

First 3 words: ["Rust", "is", "a"]
Includes 'Rust' the word: ["Rust", "Rust"]

Paragraph Analysis: Character: 39, Words: 6, Number of lines: 3, Longest Word: 'Second'

TextAnalyzer Mantém uma referência ao texto original durante todo o seu ciclo de vida <'a> — todos os métodos de análise são do tipo “zero-copy”. longest_word O valor retornado &'a str aponta para trechos de palavras no texto original e não resulta em nenhuma alocação de memória.


❓ Perguntas Frequentes

P: O tempo de vida é um conceito de tempo de execução? R: Não! O tempo de vida é um conceito exclusivamente de tempo de compilação. O compilador usa anotações de tempo de vida para verificar a validade das referências, e o executável gerado não contém nenhuma informação sobre tempo de vida — sem nenhuma sobrecarga no tempo de execução.

P: O que significa o “a” em “a”? R: É apenas um nome; é costume usar a, b, c e assim por diante. “a” é pronunciado como “tick A” ou “lifecycle A”. Você poderia muito bem escrever isso como 'my_lifetime, mas usar uma única letra é a convenção na comunidade Rust.

P: Por que as anotações de tempo de vida não são visíveis na maior parte do código em Rust? R: Por causa da regra de omissão de tempo de vida, que permite que o compilador as infira por nós. Na maioria dos casos (referências de entrada única ou métodos &self), o compilador consegue inferi-las corretamente, então você não precisa escrevê-las.

P: Qual é a relação entre o tempo de vida da referência de entrada 'a' e o valor de retorno 'a'? R: Eles assumem a interseção. O tempo de vida do valor de retorno não pode exceder o tempo de vida de nenhuma das referências de entrada. Se x tem um tempo de vida de 10 segundos e y tem um tempo de vida de 5 segundos, então 'a' tem um tempo de vida de, no máximo, 5 segundos — isso é conhecido como o “menor dos dois tempos de vida”.

P: Quando preciso definir um ciclo de vida por conta própria? R: Existem apenas três casos: quando uma função recebe várias referências de entrada e retorna uma referência, quando uma estrutura contém uma referência e quando se implementa um trait que restringe tipos associados. Em todos os outros casos, o compilador cuidará disso para você.


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Leia um trecho de código existente em Rust (como o exemplo da Lição 9) e identifique quais funções evitam a anotação manual graças à regra de elisão de tempo de vida.
  2. Dificuldade ⭐⭐⭐: Escreva uma função fn shortest<'a>(x: &'a str, y: &'a str) -> &'a str que retorne a menor de duas cadeias de caracteres. Em main, crie variáveis com tempos de vida diferentes para chamá-la.
  3. Dificuldade ⭐⭐⭐⭐: Defina uma estrutura Book<'a> que contenha um campo title do tipo &'a str. Escreva um bloco impl que implemente um método fn first_word(&self) -> &str que retorne a primeira palavra do título. Demonstre seu uso em main.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%