В Rust можно использовать конструкцию type, чтобы создать псевдоним для существующего типа. Например:
type Kilometers = i32; fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { x + y }
Kilometers — просто ещё одно имя для i32; компилятор никак не отличает эти типы, и они полностью взаимозаменяемы.
Newtype pattern — это создание новой структуры-обёртки для существующего типа:
struct Kilometers(i32); // отдельный новый тип fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { Kilometers(x.0 + y.0) }
Теперь Kilometers и i32 — разные типы, их нельзя перепутать.
Выбор между этими подходами зависит от требований к безопасности и читаемости. Для открытых API и особенно типобезопасных единиц измерения используйте newtype.
В чём разница между
typealias и newtype pattern в Rust? Можно ли ограничивать операции для псевдонима типа через реализацию трейтa?
Часто отвечают, что псевдоним можно использовать для контроля доступа и ограничения операций, но это не так — псевдоним не порождает новый тип, а значит нельзя реализовать для него уникальные трейты. Только newtype pattern — отдельный тип, для которого можно реализовать уникальное поведение.
type UserId = u64; struct UserIdNew(u64); trait Foo { fn foo(&self); } // Реализовать Foo для UserId нельзя, потому что это просто u64. // Для UserIdNew — можно.
История
В проекте измерялись расстояния в метрах и миллиметрах с помощью псевдонимов:
type Millimeters = u32; type Meters = u32;В итоговом коде случайно складывали миллиметры с метрами, что приводило к ошибкам расчётов. Типизация не сработала — компилятор никак это не отловил. Перевели на newtype и проблема исчезла.
История
В открытом API использовали псевдонимы для идентификаторов разных сущностей (
type UserId = u64;,type OrderId = u64;). Возникла путаница: кто-то перепутал параметры местами, и баг попал в прод из-за невозможности отличить их типами.
История
Разработчик пытался реализовать уникальное поведение трейта Display для псевдонима типа:
type MyType = String; impl Display for MyType { ... }Компилятор выдавал ошибку: "cannot implement trait for type alias". Проблема решилась через newtype pattern.