Traits
Инициализация типажа
Типаж нужен для организации интерфейса: он задаёт ограничения-особенности поведения для переменных или структур с неопределёнными (generic) переменными. Мы отделяем объявление типажа от его реализации. При объявлении типажа можно оставить обязательную реализацию на потом, либо вписать реализацию функций в типаже по-умолчанию:
// типаж задаёт метод и ограничения по входным/выходным типам
trait LandVehicle {
fn LandDrive(&self) -> String; }
// типаж задаёт методы плюс их реализация по умолчанию
trait WaterVehicle {
fn WaterDrive(&self) { println!("Default float"); }
}Применение типажей к структурам данных
Во время применения, если реализация по умолчанию была задана, то можно её переделать под конкретную структуру, либо использовать эту реализацию:
struct Sedan {}
struct Rocketship {}
// типаж LandVehicle не имеет реализации по умолчанию, реализуем тут
impl LandVehicle for Sedan {
fn LandDrive(&self) -> String { format!("Car zoom-zoom!") } }
// типаж WaterVehicle имеет выше реализацию по умолчанию, используем её
impl WaterVehicle for Rocketship {}Объединение типажей
При объединении, создаётся ярлык (alias). При этом каждый входящий в него типаж нужно отдельно применить к структуре данных. При этом можно также использовать реализацию определённых в типаже методов по умолчанию, либо написать свою.
// создание ярлыка
trait AmphibiousVehicle: LandVehicle + WaterVehicle {}
// применение типажей к структуре
impl AmphibiousVehicle for Carrier {}
impl LandVehicle for Carrier {
fn LandDrive(&self) -> String { format!("Use air thrust to travel on land") }
}
impl WaterVehicle for Carrier {}Вызов методов экземпляра структуры определённого типажа
fn main() {
let toyota_camry = Sedan {};
println!("{}",toyota_camry.LandDrive());
let rs = Rocketship {};
rs.WaterDrive();
let project_x = Carrier {};
println!("{}",project_x.LandDrive());
project_x.WaterDrive();
}Встроенные типажи для generic данных у функций
Типажи определяют, какие возможности есть у generic типа T:
fn my_function<T>(value: T) -> T
where
T: SomeTrait + AnotherTrait
{ /* function body */ }
Eq (Equality)
- Проверяет равенство (
==) или неравенство (!=); - Включает
PartialEq(проверяет только равенство) + гарантирует тождество;
// Без Eq типажа:
// ❌ не будет компилироваться ==
fn find_value<T>(items: &[T], target: T) -> bool {
items.iter().any(|x| x == &target) // Error
}
// Добавим Eq trait:
fn find_value<T>(items: &[T], target: T) -> bool
where
T: Eq // теперь можно использовать ==
{
items.iter().any(|x| x == &target) // ✅ работает!
}std::hash::Hash
- Позволяет конвертировать значения в хэш;
- Требуется для хранения в
HashMap,HashSet, или других коллекций с хэшом; - Тип с
Hashможет быть ключом или значением словаря:
use std::collections::HashSet;
// Без Hash:
// ❌ не будет компилироваться - HashSet требует Hash
fn create_set<T>(items: Vec<T>) -> HashSet<T> {
items.into_iter().collect() // Error: T doesn't implement Hash
}
// С Hash:
fn create_set<T>(items: Vec<T>) -> HashSet<T>
where
T: Eq + std::hash::Hash // требуется для HashSet
{
items.into_iter().collect() // ✅ работает!
}Clone
- Позволяет дублировать значение;
- Разрешает метод
.clone(); - Нужно для копирования значений без взятия владения.
// Без Clone:
fn duplicate_first<T>(items: &[T]) -> Option<T> {
items.first().map(|x| x) // Error: can't return T from &T
}
// Включаем Clone:
fn duplicate_first<T>(items: &[T]) -> Option<T>
where
T: Clone // можно клонировать
{
items.first().cloned() // ✅ работает! - создаёт копию
}Типовые комбинации Trait:
| Назначение | Типовые Traits | Пример |
|---|---|---|
| Hash collections | Eq + Hash |
HashMap<K, V>, HashSet<T> |
| Sorting/ordering | Ord or PartialOrd |
Sorting vectors, BTreeMap |
| Display/printing | Debug or Display |
println!, format! |
| Copying values | Clone or Copy |
Duplicating data |