Traits
- 定义行为在多个类型中共享。
- 可以定义默认行为在实现者中间共享。
- 可以用于定义参数的行为,同样可以定义返回值行为,当用
trait
限定返回值类型时,不能同时(if/else)返回多种实现了该trait
的类型。
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct Article{
pub title: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{}", self.title)
}
}
pub fn notify(item: impl Summary) {
println!("{}", item.summarize());
}
// trait bound 语法糖版本
pub fn notify<T: Summary>(item: T) {
println!("{}", item.summarize());
}
定义参数行为
- 通过
impl
:fn notify(item: impl TraitName)
,用于简单明了的场景,比如一个参数 - 通过
trait bound
:fn notify<T: TraitName> (item: T)
,用于更复杂的场景,比如多个参数用于减少代码-
可以通过
+
连接:fn notify(T: TraitName + Display) (item: T)
-
可以通过
where
子句fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 { // vs fn some_function<T, U>(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug {
-
Trait Objects
对比泛型:
- 泛型会在编译期展开:将确定的类型替换泛型参数展开成非泛型的实现。方法调用在编译期就能确定。 – 静态分配
Trait Object
在编译期确定方法调用。 – 动态分配
只能使用 对象安全(object-safe)
的特性作为 Trait Object
。对象安全的特性定义的所以方法必须满足如下规则:
- 所有方法返回类型不能是
Self
- 所有方法不包含泛型形参
黄金规则:我们必须将一些动态大小的类型的值放在指针后面,通过指针引用。
每一个 trait
都是一个动态大小的类型,如果要将 trait
当作对象使用必须通过指针引用,如:
&dyn Trait
Box<dyn Trait>
Rc<dyn Trait>
Sized
特性用于标志类型大小是否编译期可知,并且在编译期自动为所有内容都实现。
fn generic<T>(t: T) {
// --snip--
}
// 等于
fn generic<T: Sized>(t: T) {
// --snip--
}
可以通过 ?Sized
来避免这种默认行为:
fn generic<T: ?Sized>(t: &T) {
// --snip--
}