En Rust no hay constructores tradicionales como en C++ o Java, pero para crear objetos de tipos generalmente se utilizan funciones asociadas (a menudo llamadas new) y los llamados métodos de fábrica. Esto se debe a la historia del lenguaje, donde se presta especial atención a la seguridad y la claridad de la inicialización: solo una función escrita y llamada explícitamente es responsable de la correcta inicialización de cada campo de la estructura.
Historia de la pregunta
Inicialmente, en Rust la inicialización de estructuras permitía la asignación directa de todos los campos (sintaxis conocida como "struct literal"). Sin embargo, para asegurar la invarianza, ocultar detalles y realizar verificaciones adicionales, se practica el uso de métodos de fábrica (impl SomeStruct { fn new(...) -> Self { ... } }) o incluso la generalización a través de plantillas (patrón builder).
Problema
Las principales tareas son evitar la creación de objetos parcialmente inicializados y hacer que sea imposible utilizar estructuras en un estado inválido. Esto es especialmente crítico para estructuras complejas (por ejemplo, las relacionadas con recursos, como archivos, sockets, etc.), donde la inicialización manual de todos los campos puede llevar a errores.
Solución
En Rust se recomienda crear métodos de fábrica que devuelvan un objeto completamente inicializado, realicen validaciones cuando sea necesario y oculten los detalles de instanciación.
Ejemplo de código:
struct User { username: String, age: u8, } impl User { pub fn new(username: String, age: u8) -> Option<Self> { if age >= 18 { Some(Self { username, age }) } else { None } } } fn main() { let user = User::new("Alice".to_string(), 20); // user: Option<User>, manejar el error de forma segura }
Características clave:
fn new).¿Se pueden hacer campos privados en la estructura, de modo que no se puedan crear instancias directamente fuera del módulo?
Sí, si todos los campos de la estructura se hacen privados y solo se proporcionan métodos de fábrica públicos, la estructura no puede ser inicializada directamente fuera de su módulo.
¿Siempre debe llamarse método de fábrica new?
No, es una convención, pero no es obligatorio. Para diferentes estrategias de inicialización se utilizan nombres como "with_capacity", "from_config", "from_env", etc.
¿Pueden haber constructores privados?
Sí, si la función asociada se declara como fn new(...) -> Self sin el modificador pub, no se podrá invocar fuera de ese módulo. Esto permite, por ejemplo, implementar singleton, factory forzada o inicialización oculta.
Uso directo de una estructura con campos abiertos, sin método de fábrica:
struct Connection { fd: i32, timeout: u64, } let c = Connection { fd: -1, timeout: 10 }; // fd: -1 es inválido para el descriptor!
Ventajas:
Desventajas:
Uso de campos privados y método de fábrica:
pub struct Connection { fd: i32, timeout: u64, } impl Connection { pub fn new(fd: i32, timeout: u64) -> Option<Self> { if fd >= 0 { Some(Self { fd, timeout }) } else { None } } }
Ventajas:
Desventajas: