Explorando o Conceito 'Static' do Rust

Beginner

This tutorial is from open-source community. Access the source code

Introdução

Neste laboratório, discutiremos o conceito de 'static em Rust.

Nota: Se o laboratório não especificar um nome de arquivo, você pode usar qualquer nome de arquivo que desejar. Por exemplo, você pode usar main.rs, compilar e executá-lo com rustc main.rs && ./main.

Static (Estático)

Rust possui alguns nomes de tempo de vida (lifetime) reservados. Um deles é 'static. Você pode encontrá-lo em duas situações:

// Uma referência com tempo de vida 'static:
let s: &'static str = "hello world";

// 'static como parte de uma restrição de trait (trait bound):
fn generic<T>(x: T) where T: 'static {}

Ambos estão relacionados, mas sutilmente diferentes, e esta é uma fonte comum de confusão ao aprender Rust. Aqui estão alguns exemplos para cada situação:

Tempo de vida da referência (Reference lifetime)

Como um tempo de vida de referência, 'static indica que os dados apontados pela referência vivem durante todo o tempo de vida do programa em execução. Ele ainda pode ser coagido a um tempo de vida mais curto.

Existem duas maneiras de criar uma variável com tempo de vida 'static, e ambas são armazenadas na memória somente leitura do binário:

  • Crie uma constante com a declaração static.
  • Crie um literal string que possui o tipo: &'static str.

Veja o exemplo a seguir para uma demonstração de cada método:

// Crie uma constante com tempo de vida `'static`.
static NUM: i32 = 18;

// Retorna uma referência a `NUM` onde seu tempo de vida `'static`
// é coagido ao do argumento de entrada.
fn coerce_static<'a>(_: &'a i32) -> &'a i32 {
    &NUM
}

fn main() {
    {
        // Crie um literal `string` e imprima-o:
        let static_string = "I'm in read-only memory";
        println!("static_string: {}", static_string);

        // Quando `static_string` sai do escopo, a referência
        // não pode mais ser usada, mas os dados permanecem no binário.
    }

    {
        // Crie um inteiro para usar para `coerce_static`:
        let lifetime_num = 9;

        // Coagir `NUM` ao tempo de vida de `lifetime_num`:
        let coerced_static = coerce_static(&lifetime_num);

        println!("coerced_static: {}", coerced_static);
    }

    println!("NUM: {} permanece acessível!", NUM);
}

Restrição de trait (Trait bound)

Como uma restrição de trait, isso significa que o tipo não contém nenhuma referência não estática. Ex. o receptor pode manter o tipo pelo tempo que quiser e ele nunca se tornará inválido até que o descarte.

É importante entender que isso significa que quaisquer dados próprios (owned data) sempre passam por uma restrição de tempo de vida 'static, mas uma referência a esses dados próprios geralmente não:

use std::fmt::Debug;

fn print_it( input: impl Debug + 'static ) {
    println!( "'static value passed in is: {:?}", input );
}

fn main() {
    // i é próprio (owned) e não contém referências, portanto, é 'static:
    let i = 5;
    print_it(i);

    // ops, &i só tem o tempo de vida definido pelo escopo de
    // main(), então não é 'static:
    print_it(&i);
}

O compilador dirá:

error[E0597]: `i` does not live long enough
  --> src/lib.rs:15:15
   |
15 |     print_it(&i);
   |     ---------^^--
   |     |         |
   |     |         borrowed value does not live long enough
   |     argument requires that `i` is borrowed for `'static`
16 | }
   | - `i` dropped here while still borrowed

Resumo

Parabéns! Você concluiu o laboratório Static. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.