Rust no tiene constructores especiales como, por ejemplo, en C++/Java. Normalmente, un constructor se implementa como una función asociada (por ejemplo, new). Características importantes:
::.Result<Self, E> — útil para constructores que pueden fallar.with_capacity, from_str, variantes personalizadas).Ejemplo de un constructor básico:
struct Point { x: i32, y: i32 } impl Point { pub fn new(x: i32, y: i32) -> Self { Point { x, y } } }
Ejemplo de un constructor que puede fallar:
impl Point { pub fn try_new(x: i32, y: i32) -> Result<Self, String> { if x < 0 || y < 0 { Err("Las coordenadas deben ser no negativas".to_string()) } else { Ok(Point { x, y }) } } }
¿Qué tipo de función debe utilizarse para un constructor: función normal, método o función asociada, y por qué?
Muchos responden: "método". Pero en Rust los constructores son solo funciones asociadas (sin self), ya que el ejemplar aún no existe y no se puede llamar a un método normal.
struct Foo; impl Foo { // función asociada, no método pub fn new() -> Self { Foo } }
Historia
Un programador implementó el constructor como método:
impl Point { pub fn make(&self, x: i32, y: i32) -> Self { ... } }Esto es ilógico y confunde: ¡se necesita un objeto para crear un objeto que aún no existe! El desarrollador se perdió sobre cómo llamar a este método. Se corrigió a una función estática.
Historia
Error clásico: devolver Option<Self> para un constructor fallido sin advertencia, lo que provoca omitir errores en cascada. Después de regresar a Result<Self, E>, se volvió posible mostrar los errores de manera más explícita.
Historia
Implementación de fábricas como
from_parts,from_strsin el uso del trait From/FromStr. Como resultado, no funcionaron los mecanismos estándar de conversiones (por ejemplo,str.parse::<MyType>()). Después de detectar el error, se incorporó FromStr y se mejoró la compatibilidad del código con las bibliotecas de la ecosistema.