Trait
Syntax
Trait →
unsafe? trait IDENTIFIER GenericParams? ( : Bounds? )? WhereClause?
{
InnerAttribute*
AssociatedItem*
}
trait 描述了类型可以实现的抽象接口。此接口由关联项组成,有三种类型:
trait 声明在其所在的模块或块的类型命名空间中定义 trait。
关联项在其各自的命名空间中定义为 trait 的成员。关联类型在类型命名空间中定义。关联常量和关联函数在值命名空间中定义。
所有 trait 都定义了一个隐式类型参数 Self,它指的是“正在实现此接口的类型“。Trait 也可以包含额外的类型参数。这些类型参数(包括 Self)可以像往常一样被其他 trait 约束。
Trait 通过单独的实现为特定类型实现。
Trait 函数可以通过用分号替换函数主体来省略函数主体。这表示实现必须定义该函数。如果 trait 函数定义了主体,则此定义充当任何未覆盖它的实现的默认值。类似地,关联常量可以省略等号和表达式以表示实现必须定义常量值。关联类型必须永远不定义类型,类型只能在实现中指定。
#![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) {}
}
}
Trait 函数不允许是 const。
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);
}
}
Dyn 兼容性
dyn 兼容的 trait 可以是 trait 对象的基础 trait。如果 trait 具有以下品质,则它是dyn 兼容的:
- 所有超级 trait 也必须是 dyn 兼容的。
Sized不能是超级 trait。换句话说,它不能要求Self: Sized。
- 它不能有任何关联常量。
- 它不能有任何带泛型的关联类型。
- 所有关联函数必须可以从 trait 对象分派,或者显式不可分派:
- 可分派函数必须:
- 显式不可分派函数要求:
- 具有
where Self: Sized约束(Self类型的接收者(即self)暗示了这一点)。
- 具有
AsyncFn、AsyncFnMut和AsyncFnOncetrait 不是 dyn 兼容的。
Note
此概念以前称为对象安全性。
#![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 cannot be dispatched on
}
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
}
超级 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
// so algebraically,
// 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) {
// Here we call the area method from the supertrait `Shape` of `Circle`.
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();
}
不安全的 trait
以 unsafe 关键字开头的 trait 项表示实现该 trait 可能是不安全的。使用正确实现的不安全 trait 是安全的。Trait 实现也必须以 unsafe 关键字开头。
参数模式
没有主体的关联函数中的参数只允许 IDENTIFIER 或 _ 通配符模式,以及 SelfParam 允许的形式。mut IDENTIFIER 目前允许,但已弃用,将来会成为硬错误。
#![allow(unused)]
fn main() {
trait T {
fn f1(&self);
fn f2(x: Self, _: i32);
}
}
#![allow(unused)]
fn main() {
trait T {
fn f2(&x: &i32); // ERROR: patterns aren't allowed in functions without bodies
}
}
有主体的关联函数中的参数只允许不可反驳的模式。
#![allow(unused)]
fn main() {
trait T {
fn f1((a, b): (i32, i32)) {} // OK: is irrefutable
}
}
#![allow(unused)]
fn main() {
trait T {
fn f1(123: i32) {} // ERROR: pattern is refutable
fn f2(Some(x): Option<i32>) {} // ERROR: pattern is refutable
}
}
2018 Edition differences
在 2018 版本之前,关联函数参数的模式是可选的:
#![allow(unused)] fn main() { // 2015 Edition trait T { fn f(i32); // OK: parameter identifiers are not required } }从 2018 版本开始,模式不再是可选的。
2018 Edition differences
在 2018 版本之前,有主体的关联函数中的参数仅限于以下类型的模式:
- IDENTIFIER
mutIDENTIFIER_&IDENTIFIER&&IDENTIFIER#![allow(unused)] fn main() { // 2015 Edition trait T { fn f1((a, b): (i32, i32)) {} // ERROR: pattern not allowed } }从 2018 版本开始,所有不可反驳的模式都如 items.traits.params.patterns-with-body 中所述被允许。
项可见性
Trait 项在语法上允许使用 Visibility 标注,但在验证 trait 时会被拒绝。这允许在使用它们的不同上下文中使用统一的语法解析项。例如,空的 vis 宏片段说明符可用于 trait 项,其中宏规则可能在允许可见性的其他情况下使用。
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();
}