Что такое трейт?
Не совсем понимаю, что есть "трейт". Есть бэкграунд из ООП, но там я с таким понятием не встречался.
Интересует понятие трейта в целом, и в частности (если есть какие-то особенности, о которых нужно знать), для языка Rust.
Ищу объяснение простым языком. Очень желательно с примерами применения и реализации.
Ответы (2 шт):
Конечно, советую первоисточник: 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 фолинат = Фолиант {};
напечатать_обзор(&фолинат);
напечатать_ценный_обзор(&фолинат);
Печатает, соответственно:
Класс!
что-то интересное и очень ценное...
trait - это типа абстрактного класса, но только без полей, только методы (в том числе "абстрактные").