En Rust se puede usar la construcción type para crear un alias para un tipo existente. Por ejemplo:
type Kilometers = i32; fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { x + y }
Kilometers es simplemente otro nombre para i32; el compilador no distingue entre estos tipos, y son completamente intercambiables.
Newtype pattern es crear una nueva estructura de envoltura para un tipo existente:
struct Kilometers(i32); // tipo nuevo separado fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { Kilometers(x.0 + y.0) }
Ahora Kilometers y i32 son tipos diferentes, no se pueden confundir.
La elección entre estos enfoques depende de los requisitos de seguridad y legibilidad. Para APIs abiertas y especialmente para unidades de medida seguras en tipos, utiliza newtype.
¿Cuál es la diferencia entre el alias
typey el patrón newtype en Rust? ¿Se pueden restringir las operaciones para un alias de tipo mediante la implementación de un trait?
A menudo se responde que se puede usar un alias para controlar el acceso y restringir operaciones, pero eso no es así: el alias no genera un nuevo tipo, por lo que no se pueden implementar traits únicos para él. Solo el patrón newtype es un tipo separado, para el cual se puede implementar un comportamiento único.
type UserId = u64; struct UserIdNew(u64); trait Foo { fn foo(&self); } // No se puede implementar Foo para UserId porque es simplemente u64. // Para UserIdNew sí se puede.
Historia
En un proyecto se medían distancias en metros y milímetros usando alias:
type Millimeters = u32; type Meters = u32;En el código final se sumaron accidentalmente milímetros con metros, lo que llevó a errores de cálculo. La tipificación no funcionó: el compilador no lo detectó. Se cambió a newtype y el problema desapareció.
Historia
En una API abierta se usaron alias para identificadores de diferentes entidades (
type UserId = u64;,type OrderId = u64;). Hubo confusión: alguien confundió los parámetros y un bug llegó a producción por la imposibilidad de distinguirlos por tipos.
Historia
Un desarrollador intentó implementar un comportamiento único del trait Display para un alias de tipo:
type MyType = String; impl Display for MyType { ... }El compilador dio un error: "no se puede implementar un trait para un alias de tipo". El problema se resolvió mediante el patrón newtype.