W Rust można użyć konstrukcji type, aby stworzyć alias dla istniejącego typu. Na przykład:
type Kilometry = i32; fn dodaj_odleglosc(x: Kilometry, y: Kilometry) -> Kilometry { x + y }
Kilometry to po prostu inna nazwa dla i32; kompilator nie odróżnia tych typów, więc są całkowicie wymienne.
Newtype pattern polega na stworzeniu nowej struktury-opakowania dla istniejącego typu:
struct Kilometry(i32); // oddzielny nowy typ fn dodaj_odleglosc(x: Kilometry, y: Kilometry) -> Kilometry { Kilometry(x.0 + y.0) }
Teraz Kilometry i i32 to różne typy, nie można ich pomylić.
Wybór między tymi podejściami zależy od wymagań dotyczących bezpieczeństwa i czytelności. Dla otwartych API, a zwłaszcza typowo bezpiecznych jednostek miary, używaj newtype.
Jaka jest różnica między aliasem
typea newtype pattern w Rust? Czy można ograniczać operacje dla aliasu typu poprzez implementację traita?
Często odpowiada się, że alias można wykorzystać do kontroli dostępu i ograniczania operacji, ale to nieprawda — alias nie tworzy nowego typu, co oznacza, że nie można zaimplementować dla niego unikalnych traitów. Tylko newtype pattern reprezentuje oddzielny typ, dla którego można zaimplementować unikalne zachowanie.
type UserId = u64; struct UserIdNowy(u64); trait Foo { fn foo(&self); } // Nie można zaimplementować Foo dla UserId, ponieważ to po prostu u64. // Dla UserIdNowy — można.
Historia
W projekcie mierzono odległości w metrach i milimetrach za pomocą aliasów:
type Milimetry = u32; type Metry = u32;W końcowym kodzie przypadkowo dodawano milimetry do metrów, co prowadziło do błędów w obliczeniach. Typizacja nie zadziałała — kompilator nie wykrył tego w żaden sposób. Przeszliśmy na newtype i problem zniknął.
Historia
W otwartym API używano aliasów dla identyfikatorów różnych encji (
type UserId = u64;,type OrderId = u64;). Powstało zamieszanie: ktoś pomylił parametry miejscami, a błąd trafił do produkcji przez niemożność rozróżnienia ich typami.
Historia
Programista próbował zaimplementować unikalne zachowanie traita Display dla aliasu typu:
type MyType = String; impl Display for MyType { ... }Kompilator zgłosił błąd: "cannot implement trait for type alias". Problem został rozwiązany przez newtype pattern.