Traits
语法
Trait :
unsafe
?trait
标识符 泛型参数组? (:
类型参数约束组? )? Where从句?{
内部属性*
关联条目*
}
trait 描述了类型可以实现的抽象接口。这个接口由 关联条目 组成,包括以下三种:
所有 trait 都定义了一个隐式类型参数 Self
,该参数指代 "正在实现此接口的类型" 。
Trait 还可以包含其他类型参数。这些类型参数以及 Self
像 泛型 可以接受其他 trait 约束。
Trait 通过不同的 实现 与特定的类型关联。
Trait 函数可以省略函数体,用分号代替。这表示实现必须定义该函数。 如果 trait 函数定义了函数体,则作为未覆盖此函数实现时的默认值。 同样地,关联常量可以省略等号和表达式,以表示实现必须定义常量值。 关联类型绝不能定义类型,类型只能在实现中指定。
#![allow(unused)] fn main() { // 有定义和无定义的 trait 关联条目的示例。 trait Example { const CONST_NO_DEFAULT: i32; const CONST_WITH_DEFAULT: i32 = 99; type TypeNoDefault; fn method_without_default(&self); fn method_with_default(&self) {} } }
Trait 约束
泛型条目可以使用 trait 作为其类型参数的 约束 。
泛型 Trait
可以在 Trait 名称后指定类型参数 ,使 Trait 泛化。 参数语法与 泛型函数 的相同。
#![allow(unused)] fn main() { trait Seq<T> { fn len(&self) -> u32; fn elt_at(&self, n: u32) -> T; fn iter<F>(&self, f: F) where F: Fn(T); } }
对象安全
对象安全的 trait 可以作为 trait 对象 的基础 trait 。 如果具有以下特性 (在RFC 255中定义) ,则 trait 是 对象安全 的:
- 所有的 父级traits 也必须是对象安全的。
Sized
不能是 父级traits 。换句话说,必须不要求Self: Sized
。- 不能有任何关联常量。
- 不能有任何泛型关联类型。
- 所有关联函数必须是可从 trait 对象中可派发的,或者是明确不可派发的。
#![allow(unused)] fn main() { use std::rc::Rc; use std::sync::Arc; use std::pin::Pin; // 对象安全方法的示例。 trait TraitMethods { fn by_ref(self: &Self) {} fn by_ref_mut(self: &mut Self) {} fn by_box(self: Box<Self>) {} fn by_rc(self: Rc<Self>) {} fn by_arc(self: Arc<Self>) {} fn by_pin(self: Pin<&Self>) {} fn with_lifetime<'a>(self: &'a Self) {} fn nested_pin(self: Pin<Arc<Self>>) {} } struct S; impl TraitMethods for S {} let t: Box<dyn TraitMethods> = Box::new(S); }
#![allow(unused)] fn main() { // 这个 trait 是对象安全的,但这些方法不能在 trait 对象上派发。 trait NonDispatchable { // 非方法,不能被派发。 fn foo() where Self: Sized {} // Self 类型只有在运行时才知道。 fn returns(&self) -> Self where Self: Sized; // `other` 可能是接收者的不同的具体类型。 fn param(&self, other: Self) where Self: Sized {} // 泛型与虚表不兼容。 fn typed<T>(&self, x: T) where Self: Sized {} } struct S; impl NonDispatchable for S { fn returns(&self) -> Self where Self: Sized { S } } let obj: Box<dyn NonDispatchable> = Box::new(S); obj.returns(); // ERROR: 不能调用 Self 返回 obj.param(S); // ERROR: 不能调用 Self 参数 obj.typed(1); // ERROR: 不能调用泛型类型 }
#![allow(unused)] fn main() { use std::rc::Rc; // 非对象安全 trait 的示例。 trait NotObjectSafe { const CONST: i32 = 1; // ERROR: 不能有关联 const fn foo() {} // ERROR: 关联函数没有 Sized fn returns(&self) -> Self; // ERROR: Self 在返回类型 fn typed<T>(&self, x: T) {} // ERROR: 有泛型类型参数 fn nested(self: Rc<Box<Self>>) {} // ERROR: 尚不支持嵌套的接收者 } struct S; impl NotObjectSafe for S { fn returns(&self) -> Self { S } } let obj: Box<dyn NotObjectSafe> = Box::new(S); // ERROR }
#![allow(unused)] fn main() { // Self: Sized traits 不是 object-safe. trait TraitWithSize where Self: Sized {} struct S; impl TraitWithSize for S {} let obj: Box<dyn TraitWithSize> = Box::new(S); // ERROR }
#![allow(unused)] fn main() { // 如果 `Self` 是类型参数,则不是对象安全的。 trait Super<A> {} trait WithSelf: Super<Self> where Self: Sized {} struct S; impl<A> Super<A> for S {} impl WithSelf for S {} let obj: Box<dyn WithSelf> = Box::new(S); // ERROR: 不能使用 `Self` 类型的参数 }
父级Trait
父级trait 是指在一个类型实现某个特定 trait 之前必须实现的一些 trait。 此外,在 泛型 或 trait 对象 受到 trait 约束时,它可以访问其父级trait的关联条目。
父级 Trait 可以通过在 trait 的 Self
类型上使用 trait 约束声明,而父级 Trait 的约束将在其内部声明的 trait 的父级 Trait 中传递。trait 不能是自己的父级 Trait ,否则是错误。
具有父级 Trait 的 trait 被称为其父级 Trait 的 子trait 。
以下是将 Shape
声明为 Circle
的父级trait的示例。
#![allow(unused)] fn main() { trait Shape { fn area(&self) -> f64; } trait Circle : Shape { fn radius(&self) -> f64; } }
以下是相同的示例, 但使用 where 从句 。
#![allow(unused)] fn main() { trait Shape { fn area(&self) -> f64; } trait Circle where Self: Shape { fn radius(&self) -> f64; } }
下面的例子使用 Shape
中的 area
函数为 radius
提供了一个默认的实现。
#![allow(unused)] fn main() { trait Shape { fn area(&self) -> f64; } trait Circle where Self: Shape { fn radius(&self) -> f64 { // A = pi * r^2 // 对于代数, // r = sqrt(A / pi) (self.area() /std::f64::consts::PI).sqrt() } } }
这个例子展示了在一个泛型参数上调用父级trait 方法。
#![allow(unused)] fn main() { trait Shape { fn area(&self) -> f64; } trait Circle : Shape { fn radius(&self) -> f64; } fn print_area_and_radius<C: Circle>(c: C) { // 这里从 `Circle` 的 supertrait `Shape` 中调用 area 方法。 println!("Area: {}", c.area()); println!("Radius: {}", c.radius()); } }
类似地,以下是在 trait 对象上调用父级trait 方法的示例。
#![allow(unused)] fn main() { trait Shape { fn area(&self) -> f64; } trait Circle : Shape { fn radius(&self) -> f64; } struct UnitCircle; impl Shape for UnitCircle { fn area(&self) -> f64 { std::f64::consts::PI } } impl Circle for UnitCircle { fn radius(&self) -> f64 { 1.0 } } let circle = UnitCircle; let circle = Box::new(circle) as Box<dyn Circle>; let nonsense = circle.radius() * circle.area(); }
Unsafe traits
Trait 中以 unsafe
关键字开头的条目表示实现该 trait 可能是非安全的。
使用正确实现的非安全 trait 是安全的。相应的 trait 实现 也必须以 unsafe
关键字开头。
Sync
和 Send
是 unsafe trait 的示例。
参数模式
未设置函数或方法的实现体只允许使用 标识符 或者 _
通配符模式。
目前允许使用 mut
标识符,但是这种方式已经被弃用,并且将来会变成一个严格的错误。
在 2015 版中,trait 函数或方法参数的模式是可选的。
#![allow(unused)] fn main() { // 2015 版 trait T { fn f(i32); // 不需要参数标识符。 } }
函数或方法的参数模式种类被限制为以下之一:
从 2018 版开始,函数或方法参数模式不再是可选的。 此外,只要有函数体,所有不可拒绝模式都是允许的。 如果没有函数体,则仍然受到上述限制。
#![allow(unused)] fn main() { trait T { fn f1((a, b): (i32, i32)) {} fn f2(_: (i32, i32)); // 没有主体不能使用元组模式。 } }
条目可见性
Trait 中的条目在语法上允许添加 可见性 注解,但是当验证该 trait 时,这些注解会被拒绝。
这使得在使用这些条目的不同上下文中,可以使用统一的语法进行解析。
例如,可以使用一个空的 vis
宏片段规格来表示 trait 条目,在其他允许使用可见性的情况下使用该宏规则。
macro_rules! create_method { ($vis:vis $name:ident) => { $vis fn $name(&self) {} }; } trait T1 { // 允许使用空的 `vis` 。 create_method! { method_of_t1 } } struct S; impl S { // 这里允许可见性。 create_method! { pub method_of_s } } impl T1 for S {} fn main() { let s = S; s.method_of_t1(); s.method_of_s(); }