Придание дополнительного смысла типу переменной. Например, строка String может быть использована для чего угодно, а нам нужно конкретизировать:
// Обычный String
letname=String::from("john");// Newtypes - каждый тип уникален
structUsername(String);structEmail(String);structPassword(String);letuser=Username(String::from("john"));// Не перепутать типы
letemail=Email(String::from("john@example.com"));// Они разные!
У данного паттерна НЕТ стоимости в скорости или ресурсах приложения.
Применение
Защита от смешивания типов:
structMeters(f32);structKilometers(f32);fnrace_distance(distance: Meters){println!("Race is {} meters!",distance.0);}letm=Meters(100.0);letkm=Kilometers(0.1);race_distance(m);// ✅ Работает!
race_distance(km);// ❌ ОШИБКА! Нельзя передать КМ туда, где ждут М
pubstructCreditCardNumber(String);// клиенты видят этот метод
implCreditCardNumber{// Отдаём только безопасные методы
pubfnlast_four(&self)-> String{self.0[self.0.len()-4..].to_string()}// Внутренняя валидация происходит тут:
pubfnnew(number: String)-> Result<Self,String>{ifnumber.len()==16{Ok(CreditCardNumber(number))}else{Err("Must be 16 digits".to_string())}}}// Нельзя извне обратиться к сырой строке!
letcard=CreditCardNumber::new("1234567890123456".to_string()).unwrap();println!("{}",card.last_four());// "3456" ✅
// println!("{}", card.0); ❌ ОШИБКА! Приватное поле!
Когда использовать паттерн Newtype
Когда есть два значения одного типа, но означают разные вещи (IDs, измерения);
Когда надо добавить методы к типу, которым мы не владеем (например, обернуть u32, чтобы добавить is_even());
Когда надо применить правила валидации.
Когда НЕ использовать паттерн Newtype
Когда действительно нужны все методы исходного типа (вместо этого используйте псевдоним типа: type Age = u8).