Rust中的特征和其他语言的接口非常相似。
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct Post {
pub title: String, // 标题
pub author: String, // 作者
pub content: String, // 内容
}
impl Summary for Post {
fn summarize(&self) -> String {
format!("文章{}, 作者是{}", self.title, self.author)
}
}
pub struct Weibo {
pub username: String,
pub content: String
}
impl Summary for Weibo {
fn summarize(&self) -> String {
format!("{}发表了微博{}", self.username, self.content)
}
}
作为方法的参数使用
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
其实这个就等效于特征约束
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
以上两者是相同的。
作为方法的返回值
如果想返回一个对象,这个对象实现了某一个特征。
fn returns_summarizable() -> impl Summary {
Weibo {
username: String::from("sunface"),
content: String::from(
"m1 max太厉害了,电脑再也不会卡",
)
}
}
但是有个限制,比如碰到如下的代码就会报错:
fn returns_summarizable(switch: bool) -> impl Summary {
if switch {
Post {
title: String::from(
"Penguins win the Stanley Cup Championship!",
),
author: String::from("Iceburgh"),
content: String::from(
"The Pittsburgh Penguins once again are the best \
hockey team in the NHL.",
),
}
} else {
Weibo {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
}
}
}
也就是说作为返回值的时候,不能够返回两个不同类型的对象。
虽然他们都实现了相同的特征。
如果要实现这种返回不同对象的话,就得使用如下的代码。这里用到了特征对象。
另外一种情况,如果想要一个数组中存放不同的对象,但是这些对象都实现了相同的特征的话,那也得用到特征对象。
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct Post {
pub title: String, // 标题
pub author: String, // 作者
pub content: String, // 内容
}
impl Summary for Post {
fn summarize(&self) -> String {
format!("文章{}, 作者是{}", self.title, self.author)
}
}
pub struct Weibo {
pub username: String,
pub content: String
}
impl Summary for Weibo {
fn summarize(&self) -> String {
format!("{}发表了微博{}", self.username, self.content)
}
}
fn returns_summarizable(switch: bool) -> Box<dyn Summary> {
if switch {
Box::new(Post {
title: String::from(
"Penguins win the Stanley Cup Championship!",
),
author: String::from("Iceburgh"),
content: String::from(
"The Pittsburgh Penguins once again are the best \
hockey team in the NHL.",
),
})
} else {
Box::new(Weibo {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
})
}
}
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());
println!("{}", returns_summarizable(true).summarize());
}
存放实现了相同特征的不同对象的数组
可以使用特征对象来实现这个。
特征对象有两种实现方式。可以通过 & 引用或者 Box 智能指针的方式来创建特征对象。
- Box智能指针
如果想要一个数组同时能够存放以上两种类型的话,需要以下的定义
pub struct Screen {
pub components: Vec<Box<dyn Summary>>,
}
这个dyn就是跟特征对象配合出现的。
注意 dyn 不能单独作为特征对象的定义,例如下面的代码编译器会报错,原因是特征对象可以是任意实现了某个特征的类型,编译器在编译期不知道该类型的大小,不同的类型大小是不同的。
而 &dyn 和 Box 在编译期都是已知大小,所以可以用作特征对象的定义。
// 如下的代码编译会报错
fn draw2(x: dyn Draw) {
x.draw();
}
- &引用
总的代码如下:
trait Draw {
fn draw(&self) -> String;
}
impl Draw for u8 {
fn draw(&self) -> String {
format!("u8: {}", *self)
}
}
impl Draw for f64 {
fn draw(&self) -> String {
format!("f64: {}", *self)
}
}
// 若 T 实现了 Draw 特征, 则调用该函数时传入的 Box<T> 可以被隐式转换成函数参数签名中的 Box<dyn Draw>
fn draw1(x: Box<dyn Draw>) {
// 由于实现了 Deref 特征,Box 智能指针会自动解引用为它所包裹的值,然后调用该值对应的类型上定义的 `draw` 方法
x.draw();
}
fn draw2(x: &dyn Draw) {
x.draw();
}
fn main() {
let x = 1.1f64;
// do_something(&x);
let y = 8u8;
// x 和 y 的类型 T 都实现了 `Draw` 特征,因为 Box<T> 可以在函数调用时隐式地被转换为特征对象 Box<dyn Draw>
// 基于 x 的值创建一个 Box<f64> 类型的智能指针,指针指向的数据被放置在了堆上
draw1(Box::new(x));
// 基于 y 的值创建一个 Box<u8> 类型的智能指针
draw1(Box::new(y));
draw2(&x);
draw2(&y);
}
另外一份示例代码:
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct Post {
pub title: String, // 标题
pub author: String, // 作者
pub content: String, // 内容
}
impl Summary for Post {
fn summarize(&self) -> String {
format!("文章{}, 作者是{}", self.title, self.author)
}
}
pub struct Weibo {
pub username: String,
pub content: String
}
impl Summary for Weibo {
fn summarize(&self) -> String {
format!("{}发表了微博{}", self.username, self.content)
}
}
pub struct Screen {
pub components: Vec<Box<dyn Summary>>,
}
impl Screen {
pub fn run(&self) {
for component in self.components.iter() {
println!("{}", component.summarize());
}
}
}
fn main() {
let screen = Screen {
components: vec![
Box::new(Post{title: "Rust语言简介".to_string(),author: "Sunface".to_string(), content: "Rust棒极了!".to_string()}),
Box::new(Weibo{username: "sunface".to_string(),content: "好像微博没Tweet好用".to_string()}),
],
};
screen.run();
}