特质
语法
特质 :
unsafe
?trait
IDENTIFIER GenericParams? (:
TypeParamBounds? )? WhereClause?{
InnerAttribute*
AssociatedItem*
}
特质描述了一种类型可以实现的抽象接口。此接口包含关联项,关联项有以下三种种类
特质声明在其所在的模块或块的类型命名空间中定义了一个特质。
关联项在其各自的命名空间中定义为特质的成员。关联类型定义在类型命名空间中。关联常量和关联函数定义在值命名空间中。
所有特质都定义了一个隐式类型参数 Self
,它指代“实现此接口的类型”。特质也可能包含额外的类型参数。这些类型参数,包括 Self
,可以像通常一样受到其他特质等的约束。
特质通过单独的实现为特定类型实现。
特质函数可以通过用分号替换函数体来省略函数体。这表明实现必须定义该函数。如果特质函数定义了函数体,则此定义作为任何未覆盖它的实现的默认实现。类似地,关联常量可以省略等号和表达式,表明实现必须定义常量值。关联类型绝不能定义类型,类型只能在实现中指定。
#![allow(unused)] fn main() { // Examples of associated trait items with and without definitions. 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) {} } }
特质函数不允许是const
函数。
特质约束
泛型项可以使用特质作为其类型参数的约束。
泛型特质
可以为特质指定类型参数使其泛型化。这些参数出现在特质名称之后,使用与泛型函数中相同的语法。
#![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); } }
Dyn 兼容性
Dyn 兼容的特质可以作为特质对象的基特质。如果特质具有以下特性,则它是 dyn 兼容的
- 所有超特质也必须是 dyn 兼容的。
Sized
不能是超特质。换句话说,它不能要求Self: Sized
。
- 它不能有任何关联常量。
- 它不能有任何带有泛型的关联类型。
- 所有关联函数必须要么可从特质对象分派,要么显式地不可分派
- 可分派函数必须:
- 不能有任何类型参数(尽管允许生命周期参数)。
- 是方法,并且除了在接收者类型中使用外,不使用
Self
。 - 接收者的类型必须是以下之一:
- 不能有不透明返回类型;也就是说,
- 不能是
async fn
(它有一个隐藏的Future
类型)。 - 不能有返回位置的
impl Trait
类型(如fn example(&self) -> impl Trait
)。
- 不能是
- 不能有
where Self: Sized
约束(接收者类型为Self
(即self
) 意味着这一点)。
- 显式不可分派函数要求:
- 具有
where Self: Sized
约束(接收者类型为Self
(即self
) 意味着这一点)。
- 具有
- 可分派函数必须:
AsyncFn
、AsyncFnMut
和AsyncFnOnce
特质不是 dyn 兼容的。
注意
此概念以前称为 对象安全。
#![allow(unused)] fn main() { use std::rc::Rc; use std::sync::Arc; use std::pin::Pin; // Examples of dyn compatible methods. 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() { // This trait is dyn compatible, but these methods cannot be dispatched on a trait object. trait NonDispatchable { // Non-methods cannot be dispatched. fn foo() where Self: Sized {} // Self type isn't known until runtime. fn returns(&self) -> Self where Self: Sized; // `other` may be a different concrete type of the receiver. fn param(&self, other: Self) where Self: Sized {} // Generics are not compatible with vtables. 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: cannot call with Self return obj.param(S); // ERROR: cannot call with Self parameter obj.typed(1); // ERROR: cannot call with generic type }
#![allow(unused)] fn main() { use std::rc::Rc; // Examples of dyn-incompatible traits. trait DynIncompatible { const CONST: i32 = 1; // ERROR: cannot have associated const fn foo() {} // ERROR: associated function without Sized fn returns(&self) -> Self; // ERROR: Self in return type fn typed<T>(&self, x: T) {} // ERROR: has generic type parameters fn nested(self: Rc<Box<Self>>) {} // ERROR: nested receiver not yet supported } struct S; impl DynIncompatible for S { fn returns(&self) -> Self { S } } let obj: Box<dyn DynIncompatible> = Box::new(S); // ERROR }
#![allow(unused)] fn main() { // `Self: Sized` traits are dyn-incompatible. trait TraitWithSize where Self: Sized {} struct S; impl TraitWithSize for S {} let obj: Box<dyn TraitWithSize> = Box::new(S); // ERROR }
#![allow(unused)] fn main() { // Dyn-incompatible if `Self` is a type argument. 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: cannot use `Self` type parameter }
超特质
超特质是某个类型实现特定特质时必须实现的特质。此外,任何泛型或特质对象受到特质约束的地方,都可以访问其超特质的关联项。
超特质通过特质中 Self
类型的特质约束以及那些特质约束中声明的特质的超特质(通过传递性)来声明。特质将其自身设为其超特质是错误的。
具有超特质的特质称为其超特质的子特质。
以下是将 Shape
声明为 Circle
的超特质的示例。
#![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 // so algebraically, // r = sqrt(A / pi) (self.area() /std::f64::consts::PI).sqrt() } } }
下一个示例在泛型参数上调用了超特质方法。
#![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) { // Here we call the area method from the supertrait `Shape` of `Circle`. println!("Area: {}", c.area()); println!("Radius: {}", c.radius()); } }
类似地,这是一个在特质对象上调用超特质方法的示例。
#![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
关键字开头的特质项表示实现该特质可能是不安全的。使用正确实现的不安全特质是安全的。特质实现也必须以 unsafe
关键字开头。
参数模式
没有函数体的函数或方法声明只允许 IDENTIFIER 或 _
通配符模式。当前允许 mut
IDENTIFIER,但已弃用,将来会成为硬错误。
在 2015 版本中,特质函数或方法参数的模式是可选的
#![allow(unused)] fn main() { // 2015 Edition trait T { fn f(i32); // Parameter identifiers are not required. } }
参数的模式种类限制为以下之一:
- IDENTIFIER
mut
IDENTIFIER_
&
IDENTIFIER&&
IDENTIFIER
从 2018 版本开始,函数或方法参数模式不再是可选的。此外,只要有函数体,所有无可辩驳的模式都允许。没有函数体的情况下,上述限制仍然有效。
#![allow(unused)] fn main() { trait T { fn f1((a, b): (i32, i32)) {} fn f2(_: (i32, i32)); // Cannot use tuple pattern without a body. } }
项可见性
特质项在语法上允许Visibility注解,但在特质验证时会被拒绝。这使得项可以在不同上下文中使用统一语法进行解析。例如,可以在特质项中使用空的 vis
宏片段说明符,而该宏规则可能在允许可见性的其他情况下使用。
macro_rules! create_method { ($vis:vis $name:ident) => { $vis fn $name(&self) {} }; } trait T1 { // Empty `vis` is allowed. create_method! { method_of_t1 } } struct S; impl S { // Visibility is allowed here. create_method! { pub method_of_s } } impl T1 for S {} fn main() { let s = S; s.method_of_t1(); s.method_of_s(); }