Patrones de diseño en Rust con ejemplos: de la A a la Z

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

Patrones de diseño en Rust: de la teoría a la práctica



Los patrones de diseño son soluciones probadas para problemas típicos en el desarrollo de software. En Rust, gracias a su sistema único de propiedad (ownership) y la ausencia de POO clásica, muchos patrones clásicos adquieren un nuevo significado. En este artículo, analizaremos cuatro patrones clave: Singleton, Factory, Builder y Strategy, adaptados al Rust idiomático.



1. Singleton en Rust



En Rust no existen variables estáticas globales en el sentido habitual. Sin embargo, Singleton se puede implementar mediante lazy_static o OnceCell. Veamos un ejemplo con std::sync::OnceLock (disponible desde Rust 1.70).



Implementación con OnceLock



use std::sync::OnceLock;

static CONFIG: OnceLock<Config> = OnceLock::new();

#[derive(Debug)]struct Config { db_url: String, port: u16,}

fn get_config() -> &'static Config { CONFIG.get_or_init(|| { Config { db_url: String::from("postgres://localhost"), port: 5432, } })}

fn main() { let cfg = get_config(); println!("{:?}", cfg);}


Punto clave: OnceLock garantiza una inicialización única y seguridad en concurrencia. Esta es la forma idiomática para Rust.



2. Factory mediante traits



El patrón Factory en Rust se implementa excelentemente con traits. No necesitamos clases abstractas, basta con definir una interfaz mediante un trait.



Ejemplo: creación de vehículos



trait Transport {    fn deliver(&self);}

struct Truck;impl Transport for Truck { fn deliver(&self) { println!("Entrega en camión por tierra"); }}

struct Ship;impl Transport for Ship { fn deliver(&self) { println!("Entrega en barco por mar"); }}

struct LogisticsFactory;

impl LogisticsFactory { fn create_transport(route_type: &str) -> Box<dyn Transport> { match route_type { "land" => Box::new(Truck), "sea" => Box::new(Ship), _ => panic!("Tipo de ruta desconocido"), } }}

fn main() { let transport = LogisticsFactory::create_transport("sea"); transport.deliver();}


Ventaja: Rust no requiere herencia; el polimorfismo se logra mediante traits y Box<dyn Trait>.



3. Builder con cadena tipificada



Builder en Rust se usa ampliamente, especialmente para estructuras con muchos parámetros opcionales. Lo implementamos con una cadena tipificada de métodos que devuelven &mut Self.



Ejemplo: creación de una solicitud HTTP



#[derive(Debug, Default)]struct HttpRequest {    url: String,    method: String,    headers: Vec<(String, String)>,    body: Option<String>,}

struct HttpRequestBuilder { request: HttpRequest,}

impl HttpRequestBuilder { fn new(url: &str) -> Self { HttpRequestBuilder { request: HttpRequest { url: url.to_string(), method: "GET".to_string(), ..Default::default() }, } }

fn method(&mut self, method: &str) -> &mut Self { self.request.method = method.to_string(); self }

fn header(&mut self, key: &str, value: &str) -> &mut Self { self.request.headers.push((key.to_string(), value.to_string())); self }

fn body(&mut self, body: &str) -> &mut Self { self.request.body = Some(body.to_string()); self }

fn build(&self) -> HttpRequest { // Aquí se puede agregar validación HttpRequest { ..self.request.clone() } }}

fn ma

Blogs

Book Recommendations