第四节、特征
特征定义
Trait(特征),Rust 编译器一个特定类型具有并且可以与其他类型共享的功能。我们可以使用特征以抽象的方式定义共享行为。我们可以使用特征边界来指定泛型类型可以是具有特定行为的任何类型。
类似其他语言的接口
现在用写文章,博客或者微博的总结来做一个例子:
pub trait Summary {
fn summarize(&self) -> String;
}
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)
}
}
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.
默认实现
默认实现 你可以在特征中定义具有默认实现的方法,这样其它类型无需再实现该方法,也可以选择重载该方法实现定制:
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
pub struct Post {
pub title: String, // 标题
pub author: String, // 作者
pub content: String, // 内容
}
pub struct Weibo {
pub username: String,
pub content: String
}
impl Summary for Post {}
impl Summary for Weibo {
fn summarize(&self) -> String {
format!("{}发表了微博{}", self.username, self.content)
}
}
fn main() {
let post = Post{title: "Rust语言简介".to_string(),author: "Sunface".to_string(), content: "Rust棒极了!".to_string()};
let weibo = Weibo{username: "sunface".to_string(),content: "好像微博没Tweet好用".to_string()};
println!("{}",post.summarize());
println!("{}",weibo.summarize());
}
(Read more...)
sunface发表了微博好像微博没Tweet好用
还可以把默认方向写成下面的形式:
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
impl Summary for Weibo {
fn summarize_author(&self) -> String {
format!("{}", self.username)
}
}
....
println!("{}",weibo.summarize()); //调用的时候还是调用summarize
....
特征作为参数
参考代码:
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
pub fn notify(item1: &impl Summary, item2: &impl Summary) {
pub fn notify<T: Summary>(item1: &T, item2: &T) {
还可以使用+来使用多个特征
pub fn notify(item: &(impl Summary + Display)) {
pub fn notify<T: Summary + Display>(item: &T) {
使用过多的 trait bound 有其缺点。每个泛型都有自己的特征边界,因此具有多个泛型类型参数的函数可以在函数名称和参数列表之间包含大量特征绑定信息,从而使函数签名难以阅读。where出于这个原因,Rust 有替代语法用于在函数签名之后的子句内指定特征边界。所以不要写这个:
fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {
fn some_function<T, U>(t: &T, u: &U) -> i32
where T: Display + Clone,
U: Clone + Debug
{
函数返回值可以是实现了特征的类型
本章节前面的例子:
impl Summary for Tweet {
fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
}
}
但是,只能impl Trait在返回单一类型时使用。例如,此代码返回 aNewsArticle或 Tweet且返回类型指定为 impl Summary将不起作用:
此代码无法编译!
fn returns_summarizable(switch: bool) -> impl Summary {
if switch {
NewsArticle {
headline: String::from(
"Penguins win the Stanley Cup Championship!",
),
location: String::from("Pittsburgh, PA, USA"),
author: String::from("Iceburgh"),
content: String::from(
"The Pittsburgh Penguins once again are the best \
hockey team in the NHL.",
),
}
} else {
Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
}
}
}