Реализация трейта для типа
Теперь, когда мы определили желаемые сигнатуры методов трейта Summary
, мы можем реализовать его для типов в нашей медиа-агрегаторе. В листинге 10-13 показана реализация трейта Summary
для структуры NewsArticle
, которая использует заголовок, автора и местонахождение для создания возвращаемого значения метода summarize
. Для структуры Tweet
мы определяем summarize
как имя пользователя, за которым следует весь текст твита, предполагая, что содержание твита уже ограничено 280 символами.
Имя файла: src/lib.rs
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!(
"{}, by {} ({})",
self.headline,
self.author,
self.location
)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
Листинг 10-13: Реализация трейта Summary
для типов NewsArticle
и Tweet
Реализация трейта для типа похожа на реализацию обычных методов. Разница заключается в том, что после impl
мы указываем имя трейта, который хотим реализовать, затем используем ключевое слово for
, а затем указываем имя типа, для которого мы хотим реализовать трейт. Внутри блока impl
мы размещаем сигнатуры методов, определенные в определении трейта. Вместо того, чтобы после каждой сигнатуры добавить точку с запятой, мы используем фигурные скобки и заполняем тело метода конкретным поведением, которое мы хотим, чтобы методы трейта имели для конкретного типа.
Теперь, когда библиотека реализовала трейт Summary
для NewsArticle
и Tweet
, пользователи этого крейта могут вызывать методы трейта для экземпляров NewsArticle
и Tweet
так же, как мы вызываем обычные методы. Единственная разница заключается в том, что пользователь должен также импортировать трейт в область видимости, как и типы. Вот пример того, как бинарный крейт может использовать наш библиотечный крейт aggregator
:
use aggregator::{Summary, Tweet};
fn main() {
let tweet = Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
};
println!("1 new tweet: {}", tweet.summarize());
}
Этот код выводит 1 new tweet: horse_ebooks: of course, as you probably already know, people
.
Другие зависимости от крейта aggregator
также могут импортировать трейт Summary
в область видимости, чтобы реализовать Summary
для своих собственных типов. Одно ограничение, о котором нужно помнить, заключается в том, что мы можем реализовать трейт для типа только в том случае, если либо трейт, либо тип, или оба, локальны для нашего крейта. Например, мы можем реализовать стандартные библиотеки трейты, такие как Display
, для пользовательского типа, такого как Tweet
, как часть функциональности нашего крейта aggregator
, потому что тип Tweet
локален для нашего крейта aggregator
. Мы также можем реализовать Summary
для Vec<T>
в нашем крейте aggregator
, потому что трейт Summary
локален для нашего крейта aggregator
.
Но мы не можем реализовать внешние трейты для внешних типов. Например, мы не можем реализовать трейт Display
для Vec<T>
внутри нашего крейта aggregator
, потому что Display
и Vec<T>
определены в стандартной библиотеке и не являются локальными для нашего крейта aggregator
. Это ограничение является частью свойства, называемого согласованностью, и более конкретно правилом сироты, так называемым потому, что родительский тип отсутствует. Это правило гарантирует, что код других людей не может сломать ваш код и наоборот. Без этого правила два крейта могли бы реализовать один и тот же трейт для одного и того же типа, и Rust не знал бы, какой имплементации использовать.