# Трейты (Rust)

> Трейт в Rust описывает общее поведение — набор методов, которые тип может реализовать; основа обобщённого программирования, статической и динамической диспетчеризации.

Источник: https://llmdocs.ru/wiki/rust-traits/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Аналоги**: интерфейсы (Java, Go), классы типов (Haskell)
- **Реализация**: impl Трейт for Тип
- **Статическая диспетчеризация**: обобщения с ограничениями (T Display), impl Trait
- **Динамическая диспетчеризация**: dyn Trait через таблицу виртуальных методов
- **Автоматическая реализация**: #[derive(...)]
- **Ограничение**: правило сироты (orphan rule)
- **Стандартные трейты**: Debug, Display, Clone, Copy, From, Iterator, Send, Sync
**Трейт** (англ. *trait* — «черта», «свойство»; в русских переводах также «типаж») — механизм [Rust](https://llmdocs.ru/wiki/rust.md) для описания общего поведения: набор методов, которые разные типы могут реализовать каждый по-своему. Трейты похожи на интерфейсы в Java и [Go](https://llmdocs.ru/wiki/go.md) и на классы типов в Haskell. На них построено обобщённое программирование в Rust: функция может принимать «любой тип, который умеет выводиться на экран» или «любой итератор», а компилятор проверяет это на этапе сборки.[1]

Многие ключевые свойства языка выражены трейтами: копирование (`Copy`, `Clone`), вывод (`Debug`, `Display`), сравнение (`PartialEq`, `Ord`), итерация (`Iterator`), преобразования (`From`, `Into`) и потокобезопасность (`Send`, `Sync`).

## Определение и реализация

```rust
trait Shape {
    fn area(&self) -> f64;                       // обязательный метод
    fn describe(&self) -> String {               // метод с реализацией по умолчанию
        format!("фигура площадью {:.2}", self.area())
    }
}

struct Circle { r: f64 }
struct Rect { w: f64, h: f64 }

impl Shape for Circle {
    fn area(&self) -> f64 { std::f64::consts::PI * self.r * self.r }
}

impl Shape for Rect {
    fn area(&self) -> f64 { self.w * self.h }
    fn describe(&self) -> String { format!("прямоугольник {}×{}", self.w, self.h) }
}
```

В отличие от классического наследования, реализация трейта пишется отдельно от определения типа: можно реализовать свой трейт для чужих типов (например, для `i32` или `Vec<T>`) и чужой трейт для своих.

## Обобщения и ограничения

Трейты ограничивают параметры обобщённых функций — компилятор пропустит только подходящие типы:

```rust
use std::fmt::Display;

fn largest<T: PartialOrd + Copy>(items: &[T]) -> T {
    let mut max = items[0];
    for &x in items { if x > max { max = x; } }
    max
}

fn print_all(items: &[impl Display]) {           // сокращённая запись ограничения
    for x in items { println!("{x}"); }
}

fn total_area<S>(shapes: &[S]) -> f64
where
    S: Shape,
{
    shapes.iter().map(|s| s.area()).sum()
}
```

Обобщённый код проходит **мономорфизацию**: для каждого конкретного типа компилятор создаёт отдельную специализированную копию функции. Вызовы методов разрешаются при компиляции и могут встраиваться — это **статическая диспетчеризация** без накладных расходов во время выполнения, ценой большего размера бинарника.

## Трейт-объекты

Когда в одной коллекции нужны значения разных типов, используют **трейт-объекты** `dyn Trait`. Значение хранится за указателем (`Box`, `&`, `Arc`), а метод выбирается во время выполнения через таблицу виртуальных методов — **динамическая диспетчеризация**:[2]

```rust
let shapes: Vec<Box<dyn Shape>> = vec![
    Box::new(Circle { r: 1.0 }),
    Box::new(Rect { w: 2.0, h: 3.0 }),
];
for s in &shapes {
    println!("{}", s.describe());
}
```

| | Обобщения (`T: Trait`, `impl Trait`) | Трейт-объекты (`dyn Trait`) |
|---|---|---|
| Выбор метода | при компиляции | во время выполнения |
| Скорость | максимальная, возможна оптимизация | косвенный вызов |
| Разнородные коллекции | нет | да |
| Размер кода | растёт с числом типов | одна копия |

Не каждый трейт можно сделать объектом: например, методы с обобщёнными параметрами или возвращающие `Self` этому мешают.[3]

## Ассоциированные типы и стандартные трейты

Трейт может объявлять **ассоциированный тип**, который определяет реализация. Классический пример — `Iterator`:

```rust
struct Countdown(u32);

impl Iterator for Countdown {
    type Item = u32;                              // ассоциированный тип
    fn next(&mut self) -> Option<u32> {
        if self.0 == 0 { None } else { self.0 -= 1; Some(self.0 + 1) }
    }
}

let v: Vec<u32> = Countdown(3).filter(|x| x % 2 == 1).collect();   // [3, 1]
```

Реализовав один метод `next`, тип бесплатно получает десятки методов по умолчанию — `map`, `filter`, `sum`, `collect`.

Многие стандартные трейты можно реализовать автоматически атрибутом `#[derive]`:

```rust
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
struct UserId(u64);
```

Руководство по API для Rust рекомендует реализовывать для публичных типов стандартные трейты, где это имеет смысл, — так типы лучше сочетаются с остальной экосистемой.[6]

## Правило сироты

Реализовать трейт для типа можно, только если трейт или тип определены в вашем крейте. Это **правило сироты** (*orphan rule*) не позволяет двум библиотекам дать конфликтующие реализации одного и того же трейта для одного типа. Обходной путь — обёртка-новый тип (*newtype*): `struct Wrapper(Vec<String>);`.

## Трейты и асинхронность

С Rust 1.75 (декабрь 2023) в трейтах можно объявлять асинхронные методы `async fn` и методы, возвращающие `impl Trait`; до этого требовались обходные решения вроде крейта `async-trait` (см. [async в Rust и Tokio](https://llmdocs.ru/wiki/rust-async-tokio.md)).[5] Маркерные трейты `Send` и `Sync` сообщают компилятору, можно ли передавать значение между потоками и разделять его, — на этом основана защита от гонок данных (см. [Владение и заимствование (Rust)](https://llmdocs.ru/wiki/ownership-borrowing.md)). Больше примеров — в Rust By Example.[4]

## См. также

- [Rust (язык)](https://llmdocs.ru/wiki/rust.md)
- [Владение и заимствование (Rust)](https://llmdocs.ru/wiki/ownership-borrowing.md)
- [Обработка ошибок в Rust (Result, ?)](https://llmdocs.ru/wiki/rust-errors.md)
- [async в Rust и Tokio](https://llmdocs.ru/wiki/rust-async-tokio.md)
- [Макросы Rust](https://llmdocs.ru/wiki/rust-macros.md)

## Источники

1. [The Rust Programming Language — Traits: Defining Shared Behavior](https://doc.rust-lang.org/book/ch10-02-traits.html)
2. [The Rust Programming Language — Using Trait Objects](https://doc.rust-lang.org/book/ch18-02-trait-objects.html)
3. [The Rust Reference — Traits](https://doc.rust-lang.org/reference/items/traits.html)
4. [Rust By Example — Traits](https://doc.rust-lang.org/rust-by-example/trait.html)
5. [Rust Blog — Announcing async fn and return-position impl Trait in traits (Rust 1.75)](https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html)
6. [Rust API Guidelines — Interoperability (common traits)](https://rust-lang.github.io/api-guidelines/interoperability.html)
