Explorando el concepto de static en Rust

Beginner

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

Introducción

En este laboratorio, discutiremos el concepto de 'static en Rust.

Nota: Si el laboratorio no especifica un nombre de archivo, puede usar cualquier nombre de archivo que desee. Por ejemplo, puede usar main.rs, compilarlo y ejecutarlo con rustc main.rs &&./main.

static

Rust tiene algunos nombres de duración de vida reservados. Uno de ellos es 'static. Puede encontrarlo en dos situaciones:

// Una referencia con duración de vida `'static`:
let s: &'static str = "hello world";

// `'static` como parte de una restricción de trato:
fn genérico<T>(x: T) donde T: 'static {}

Ambas están relacionadas pero ligeramente diferentes y este es una fuente común de confusión al aprender Rust. Aquí hay algunos ejemplos para cada situación:

Duración de vida de la referencia

Como duración de vida de una referencia, 'static indica que los datos apuntados por la referencia viven durante toda la duración del programa en ejecución. Aún puede ser forzado a una duración de vida más corta.

Hay dos maneras de crear una variable con duración de vida 'static, y ambas se almacenan en la memoria de solo lectura del binario:

  • Crear una constante con la declaración static.
  • Crear un literal de string que tiene el tipo: &'static str.

Vea el siguiente ejemplo para una demostración de cada método:

// Crear una constante con duración de vida `'static`.
static NUM: i32 = 18;

// Devuelve una referencia a `NUM` donde su duración de vida `'static`
// se fuerza a la del argumento de entrada.
fn forzar_estático<'a>(_: &'a i32) -> &'a i32 {
    &NUM
}

fn main() {
    {
        // Crear un literal de `string` e imprimirlo:
        let cadena_estática = "Estoy en memoria de solo lectura";
        println!("cadena_estática: {}", cadena_estática);

        // Cuando `cadena_estática` sale del ámbito, la referencia
        // ya no se puede usar, pero los datos permanecen en el binario.
    }

    {
        // Crear un entero para usar en `forzar_estático`:
        let num_duración = 9;

        // Forzar `NUM` a la duración de vida de `num_duración`:
        let estático_forzado = forzar_estático(&num_duración);

        println!("estático_forzado: {}", estático_forzado);
    }

    println!("NUM: {} sigue siendo accesible!", NUM);
}

Restricción de trato

Como una restricción de trato, significa que el tipo no contiene ninguna referencia no estática. Es decir, el receptor puede mantener el tipo durante tanto tiempo como quiera y nunca se tornará inválido hasta que lo abandone.

Es importante entender que esto significa que cualquier dato poseído siempre pasa una restricción de duración de vida 'static, pero una referencia a ese dato poseído generalmente no:

use std::fmt::Debug;

fn imprimirlo(entrada: impl Debug + 'static) {
    println!("Valor `'static` pasado es: {:?}", entrada);
}

fn main() {
    // i es poseído y no contiene referencias, por lo tanto es `'static`:
    let i = 5;
    imprimirlo(i);

    // oops, &i solo tiene la duración de vida definida por el ámbito de
    // main(), por lo tanto no es `'static`:
    imprimirlo(&i);
}

El compilador le dirá:

error[E0597]: `i` no tiene una duración de vida lo suficientemente larga
  --> src/lib.rs:15:15
   |
15 |     imprimirlo(&i);
   |     ---------^^--
   |     |         |
   |     |         el valor prestado no tiene una duración de vida lo suficientemente larga
   |     el argumento requiere que `i` sea prestado para `'static`
16 | }
   | - `i` se descarta aquí mientras todavía está prestado

Resumen

¡Felicitaciones! Has completado el laboratorio de Static. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.