Patrones de diseño en Rust: ejemplos e implementación

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 con ejemplos



Los patrones de diseño son soluciones probadas para problemas típicos en el desarrollo de software. En Rust, debido a su sistema único de propiedad, préstamo y tiempos de vida, la implementación de los patrones clásicos de GoF a menudo requiere un enfoque especial. En este artículo, analizaremos varios patrones populares con ejemplos de código real en Rust.



¿Por qué Rust cambia el enfoque hacia los patrones?

A diferencia de los lenguajes con recolección de basura (Java, C#), Rust impone reglas estrictas sobre la propiedad de la memoria. Esto hace que algunas implementaciones clásicas (por ejemplo, Singleton a través de estado mutable global) sean inseguras o ineficientes. Sin embargo, Rust ofrece sus propias alternativas elegantes, basadas en tipos y traits.



1. Builder (Constructor) — creación paso a paso de objetos

Builder es uno de los patrones más utilizados en Rust. Es ideal para la configuración de estructuras complejas, especialmente cuando hay muchos campos opcionales.



Ejemplo: creación de una solicitud HTTP

struct Request {    method: String,    url: String,    headers: Vec<(String, String)>,    body: Option<String>,}

struct RequestBuilder { method: String, url: String, headers: Vec<(String, String)>, body: Option<String>,}

impl RequestBuilder { fn new(method: &str, url: &str) -> Self { Self { method: method.to_string(), url: url.to_string(), headers: Vec::new(), body: None, } }

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

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

fn build(self) -> Request { Request { method: self.method, url: self.url, headers: self.headers, body: self.body, } }}

fn main() { let req = RequestBuilder::new("POST", "https://api.example.com/data") .header("Content-Type", "application/json") .header("Authorization", "Bearer token123") .body(r#"{"key": "value"}"#) .build();

println!("URL: {}", req.url); println!("Headers count: {}", req.headers.len());}


Ventajas del enfoque en Rust

  • Seguridad: el objeto se crea solo después de llamar a build() — una configuración incompleta es imposible.
  • Legibilidad: la cadena de métodos es intuitivamente comprensible.
  • Seguridad de tipos: el compilador verifica los tipos en cada paso.


2. Strategy (Estrategia) — selección de algoritmo sobre la marcha

Strategy permite cambiar el comportamiento de un objeto durante la ejecución. En Rust, esto se implementa a través de traits y despacho dinámico (Box con traits) o estático (genéricos).



Ejemplo: ordenación de datos

trait SortStrategy {    fn sort(&self, data: &mut [i32]);}

struct BubbleSort;struct QuickSort;

impl SortStrategy for BubbleSort { fn sort(&self, data: &mut [i32]) { // Implementación simplificada de ordenación de burbuja for i in 0..data.len() { for j in 0..data.len() - 1 - i { if data[j] > data[j + 1] { data.swap(j, j + 1); } } } }}

impl SortStrategy for QuickSort { fn sort(&self, data: &mut [i32]) { // Ordenación rápida (simplificada) if data.len() > 1 { let pivot = data.len() / 2; data.sort(); // Usamos la incorporada para el ejemplo } }}

struct Sorter { strategy: Box<dyn SortStrategy>,}

impl Sorter { fn new(strategy: Box<dyn SortStrategy>) ->

Blogs

Book Recommendations