once_cell en Rust: guía completa con ejemplos prácticos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Biblioteca once_cell para Rust: inicialización perezosa y celdas de un solo uso

Qué es y por qué usarla

La biblioteca once_cell es una de las más populares y útiles del ecosistema Rust, diseñada para implementar el patrón de inicialización perezosa (lazy initialization) y crear celdas de un solo uso (once cells). Proporciona dos tipos clave: OnceCell y Lazy, que resuelven un problema fundamental: la creación diferida de un valor que se inicializa solo una vez al primer acceso.

¿Para qué sirve? En Rust, como en cualquier otro lenguaje, a menudo surge la necesidad de crear un objeto costoso (por ejemplo, leer un archivo de configuración, establecer una conexión a una base de datos, cargar un diccionario grande). Si ese objeto no es necesario al iniciar el programa, sino solo bajo ciertas condiciones, su inicialización puede posponerse. once_cell lo hace de forma segura y eficiente, garantizando que la inicialización ocurra exactamente una vez, incluso con acceso concurrente desde múltiples hilos.

Antes de que once_cell llegara a la biblioteca estándar de Rust (desde la versión 1.70.0 con std::cell::OnceCell y std::sync::OnceLock), esta biblioteca era el estándar de facto para la inicialización perezosa. Sigue siendo relevante hoy gracias a su madurez, amplio conjunto de funciones y compatibilidad hacia atrás. La biblioteca ofrece versiones seguras para subprocesos (sync) y de un solo hilo (unsync), lo que permite evitar la sobrecarga de sincronización cuando no es necesaria.

Instalación

Para usar once_cell, agrégalo como dependencia en tu archivo Cargo.toml:

[dependencies]
once_cell = "1.20"

O ejecuta el comando:

cargo add once_cell

Luego puedes importar los tipos necesarios:

use once_cell::sync::{OnceCell, Lazy};
// o para uso en un solo hilo:
use once_cell::unsync::{OnceCell as UnsyncOnceCell, Lazy as UnsyncLazy};

Inicio rápido

Un ejemplo mínimo funcional usando Lazy para inicialización perezosa de una variable estática:

use once_cell::sync::Lazy;
use std::collections::HashMap;

// La variable estática se inicializa solo al primer acceso
static CONFIG: Lazy<HashMap<String, String>> = Lazy::new(|| {
    println!("Inicializando configuración...");
    let mut map = HashMap::new();
    map.insert("host".to_string(), "localhost".to_string());
    map.insert("port".to_string(), "8080".to_string());
    map
});

fn main() {
    // Primer acceso: la inicialización ocurre aquí
    println!("Host: {}", CONFIG.get("host").unwrap());
    
    // Segundo acceso: NO se inicializa, se usa el valor en caché
    println!("Puerto: {}", CONFIG.get("port").unwrap());
}

Salida del programa:

Inicializando configuración...
Host: localhost
Puerto: 8080

Métodos y funciones principales

Veamos 8 métodos y funciones clave de la biblioteca once_cell con ejemplos de uso.

1. OnceCell::new()

Firma: pub const fn new() -> OnceCell<T>

Qué hace: Crea una nueva celda vacía que aún no contiene ningún valor.

use once_cell::sync::OnceCell;

let cell: OnceCell<i32> = OnceCell::new();
assert!(cell.get().is_none()); // La celda está vacía

2. OnceCell::set()

Firma: pub fn set(&self, value: T) -> Result<(), T>

Recommendations