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

Не совсем понимаю, что есть "трейт". Есть бэкграунд из ООП, но там я с таким понятием не встречался.

Интересует понятие трейта в целом, и в частности (если есть какие-то особенности, о которых нужно знать), для языка Rust.

Ищу объяснение простым языком. Очень желательно с примерами применения и реализации.


Ответы (2 шт):

Автор решения: p.k.

Конечно, советую первоисточник: https://doc.rust-lang.org/book/ch10-02-traits.html . Если простыми словами, то trait (англ. черта, признак, умение) -- определяет функциональность, которую имеет конкретный тип и которую он может иметь совместно с другими типами.

Из личного опыта, trait в Rust похож на @protocol в Objective-C. А в документации так и сказано, что trait похож на interface в других языках.

Самый простой пример:

#![allow(unused)]
trait Обзор {
    fn обзор(&self) -> String;
}

struct Книга {
    автор :  String,
    название : String,
    содержание : String,
}

impl Обзор for Книга {
    fn обзор(&self) -> String {
        format!("{}, {}", self.название, self.автор)
    }
}

fn main() {
    let повесть = Книга {
        автор : "Вася".to_string(), 
        название : "Мемуары".to_string(), 
        содержание: "Было так...".to_string()};
    println!("{}", &повесть.обзор());
}

Печатает на выходе: Мемуары, Вася

Допустим, в какой-то момент вы создали какой-либо другой тип (Инструкция, СМС, Карта, Газета). Любой из них может иметь данное "умение".

impl Обзор for Инструкция {};
impl Обзор for СМС {};
impl Обзор for Карта {};
impl Обзор for Газета {};

Более того, в Rust есть концепция lifetime ("срок жизни"), которую я не встречал ни в каких других языках (может она и есть где-то). Нет в Rust одной важной черты классического ООП -- в нём нет наследования! Т.е. никакой связи "предок-потомок". Всё определяется через traits.

Однако, можно создавать наборы из различных "умений", добавляя нечто новое. Вот пример из реальной жизни:

pub trait ID3D12Debug5_Impl: Sized + ID3D12Debug_Impl + ID3D12Debug3_Impl + ID3D12Debug4_Impl {
    fn SetEnableAutoName(&self, enable: BOOL);
}

Это вроде, как бы похоже на некое "наследование" (inheritance). Но, опять же, собственно такого понятия в Rust нет.

Очень (может быть, даже самый важный момент, точки зрения использования) -- это trait objects, "объекты с умениями". https://doc.rust-lang.org/reference/types/trait-object.html

// К коду выше...
fn напечатать_обзор(a: &dyn Обзор) {
    println!("{}", a.обзор());
}

напечатать_обзор(&повесть); // Печатает: Мемуары, Вася

// А также, если такие объекты определены:
напечатать_обзор(&инструкция);
напечатать_обзор(&смс);
напечатать_обзор(&карта);
напечатать_обзор(&газета);

Добавление о "сверх способностях" (supertraits). С помощью чего достигается некое подобие наследования.

trait Рекомендация {
    fn рекомендация(&self) -> String;
}

trait ОбзорДляЦенителей: Обзор + Рекомендация {
    fn ценный_обзор(&self) -> String;
}

Далее, как пример, использование:

struct Фолиант {}

impl Обзор for Фолиант {
    fn обзор(&self) -> String {
        "Класс!".to_string()
    }
}
impl Рекомендация for Фолиант {
    fn рекомендация(&self) -> String {
        "Точно читать.".to_string()
    }
}

// ВАЖНО! Просто соответствовать способности `ОбзорДляЦенителей` не достаточно, необходимы и `Обзор` и `Рекомендация` (выше) 
impl ОбзорДляЦенителей for Фолиант {
    fn ценный_обзор(&self) -> String {
        "что-то интересное и очень ценное...".to_string()
    }
}

fn напечатать_ценный_обзор(a: &dyn ОбзорДляЦенителей) {
    println!("{}", a.ценный_обзор());
}

И в главной функции:

let фолинат = Фолиант {};
напечатать_обзор(&фолинат);
напечатать_ценный_обзор(&фолинат);

Печатает, соответственно:

Класс!
что-то интересное и очень ценное...
→ Ссылка
Автор решения: fox

trait - это типа абстрактного класса, но только без полей, только методы (в том числе "абстрактные").

→ Ссылка