特征
语法
特征 :
unsafe
?trait
标识符 泛型参数? (:
类型参数边界? )? Where 子句?{
内部属性*
关联项*
}
特征描述了类型可以实现的抽象接口。此接口由关联项组成,关联项分为三种:
所有特征都定义了一个隐式类型参数 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); } }
对象安全
对象安全特征可以是特征对象的基础特征。如果一个特征具有以下特征(在RFC 255中定义),则它是对象安全的:
- 所有超特征也必须是对象安全的。
Sized
不能是超特征。换句话说,它不能要求Self: Sized
。- 它不能有任何关联常量。
- 它不能有任何带有泛型的关联类型。
- 所有关联函数必须可以从特征对象调度,或者显式地不可调度。
- 可调度函数必须:
- 没有任何类型参数(尽管允许使用生命周期参数)。
- 是一个方法,除了在接收器的类型中之外,不使用
Self
。 - 具有以下类型之一的接收器:
- 没有不透明的返回类型;也就是说,
- 不是
async fn
(它有一个隐藏的Future
类型)。 - 没有返回位置
impl Trait
类型(fn example(&self) -> impl Trait
)。
- 不是
- 没有
where Self: Sized
边界(Self
的接收器类型(即self
)隐含了这一点)。
- 显式不可调度函数要求:
- 具有
where Self: Sized
边界(Self
的接收器类型(即self
)隐含了这一点)。
- 具有
- 可调度函数必须:
#![allow(unused)] fn main() { use std::rc::Rc; use std::sync::Arc; use std::pin::Pin; // Examples of object safe 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 object-safe, 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 non-object safe traits. trait NotObjectSafe { 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 NotObjectSafe for S { fn returns(&self) -> Self { S } } let obj: Box<dyn NotObjectSafe> = Box::new(S); // ERROR }
#![allow(unused)] fn main() { // Self: Sized traits are not 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() { // Not object safe 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
关键字开头。
参数模式
没有主体的函数或方法声明只允许 标识符 或 _
通配符 模式。mut
标识符 当前是允许的,但它已被弃用,并将在未来成为硬错误。
在 2015 版中,特征函数或方法参数的模式是可选的
#![allow(unused)] fn main() { // 2015 Edition trait T { fn f(i32); // Parameter identifiers are not required. } }
参数的模式种类仅限于以下之一
从 2018 版开始,函数或方法参数模式不再是可选的。此外,只要有主体,就允许所有不可反驳的模式。如果没有主体,则上述限制仍然有效。
#![allow(unused)] fn main() { trait T { fn f1((a, b): (i32, i32)) {} fn f2(_: (i32, i32)); // Cannot use tuple pattern without a body. } }
项可见性
特征项在语法上允许使用 可见性 注解,但在验证特征时会拒绝此注解。这允许使用统一的语法解析不同上下文中的项。例如,空的 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(); }