Трейты (traits)

Трейты описывают общее поведение, которое могут реализовать разные типы — это интерфейсы Rust.

Трейт (trait) — набор методов, описывающих поведение; типы реализуют трейт, обещая предоставить это поведение.

Что такое трейт

Трейт — это контракт: «тип, реализующий меня, умеет делать вот это». По духу это интерфейс из других языков. Сначала объявляют трейт с сигнатурами методов, потом реализуют его для конкретных типов через impl Трейт for Тип.

trait Greet {
    fn hello(&self) -> String; // только сигнатура, без тела
}

struct Russian;
struct English;

impl Greet for Russian {
    fn hello(&self) -> String {
        String::from("Привет")
    }
}

impl Greet for English {
    fn hello(&self) -> String {
        String::from("Hello")
    }
}

fn main() {
    let r = Russian;
    let e = English;
    println!("{}", r.hello());
    println!("{}", e.hello());
}

Вывод:

Привет
Hello

Дефолтные методы

Трейт может задавать реализацию метода по умолчанию. Тогда типу не обязательно его переопределять — он получит готовое поведение, но может и заменить своим.

trait Animal {
    fn name(&self) -> String;

    // метод с реализацией по умолчанию
    fn describe(&self) -> String {
        format!("Это {}", self.name())
    }
}

struct Dog;
impl Animal for Dog {
    fn name(&self) -> String {
        String::from("собака")
    }
    // describe не переопределяем — берём дефолтный
}

fn main() {
    let d = Dog;
    println!("{}", d.describe());
}

Вывод:

Это собака

Трейты как ограничения

Главная польза трейтов — в обобщённом коде. Функция может принимать «любой тип, который реализует трейт Greet». Это записывают через impl Trait в параметре или через ограничение <T: Trait>.

trait Greet {
    fn hello(&self) -> String;
}

struct Russian;
impl Greet for Russian {
    fn hello(&self) -> String { String::from("Привет") }
}

// принимаем любой тип, реализующий Greet
fn announce(g: &impl Greet) {
    println!("Говорит: {}", g.hello());
}

fn main() {
    announce(&Russian);
}

Вывод:

Говорит: Привет

Так трейты дают полиморфизм: одна функция работает с любым типом, который умеет нужное поведение, и компилятор проверяет это заранее.

derive — реализация трейтов автоматом

Многие стандартные трейты Rust умеет реализовать за вас. Атрибут #[derive(...)] над структурой автоматически добавляет нужное поведение — без ручного impl.

#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let a = Point { x: 1, y: 2 };
    let b = a.clone();          // Clone
    println!("{:?}", a);        // Debug — печать через {:?}
    println!("равны: {}", a == b); // PartialEq — сравнение через ==
}

Вывод:

Point { x: 1, y: 2 }
равны: true

Итог

  • Трейт — это интерфейс Rust: контракт поведения, который типы реализуют через impl Trait for Тип.
  • Трейты могут давать дефолтные методы и использоваться как ограничения в обобщённом коде.
  • #[derive(...)] реализует стандартные трейты (Debug, Clone, PartialEq) автоматически.
Проверьте себя
1. Что такое трейт в Rust?
AТип данных
BКонтракт поведения (набор методов), который типы реализуют — аналог интерфейса
CПеременная
DЦикл
2. Что даёт дефолтный метод в трейте?
AЗапрещает реализацию
BГотовую реализацию, которую тип может использовать или переопределить
CУдаляет метод
DДелает метод приватным
3. Что делает атрибут #[derive(Debug, Clone)] над структурой?
AУдаляет структуру
BАвтоматически реализует указанные стандартные трейты
CОбъявляет переменные
DЗапускает тесты
Поддержать проект