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() {
// 带有定义和无定义的关联 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 函数不允许是 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;
// dyn 兼容方法的示例。
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 是 dyn 兼容的,但这些方法不能在 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(); // 错误:不能用 Self 返回值调用
obj.param(S); // 错误:不能用 Self 参数调用
obj.typed(1); // 错误:不能用泛型类型调用
}
#![allow(unused)]
fn main() {
use std::rc::Rc;
// dyn 不兼容 trait 的示例。
trait DynIncompatible {
const CONST: i32 = 1; // 错误:不能有关联常量
fn foo() {} // 错误:不带 Sized 的关联函数
fn returns(&self) -> Self; // 错误:返回类型中的 Self
fn typed<T>(&self, x: T) {} // 错误:有泛型类型参数
fn nested(self: Rc<Box<Self>>) {} // 错误:嵌套接收器不能在其上分派
}
struct S;
impl DynIncompatible for S {
fn returns(&self) -> Self { S }
}
let obj: Box<dyn DynIncompatible> = Box::new(S); // 错误
}
#![allow(unused)]
fn main() {
// `Self: Sized` trait 是 dyn 不兼容的。
trait TraitWithSize where Self: Sized {}
struct S;
impl TraitWithSize for S {}
let obj: Box<dyn TraitWithSize> = Box::new(S); // 错误
}
#![allow(unused)]
fn main() {
// 如果 `Self` 是类型参数,则 dyn 不兼容。
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); // 错误:不能使用 `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` 的超 trait `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 trait
以 unsafe 关键字开头的 trait 程序项表示实现该 trait 可能是不安全的。使用正确实现的 unsafe trait 是安全的。trait 实现也必须以 unsafe 关键字开头。
Sync 和 Send 是 unsafe trait 的示例。
参数模式
不带主体的关联函数中的参数只允许使用 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); // 错误:不带主体的函数中不允许使用模式
}
}
带主体的关联函数中的参数只允许使用不可反驳的模式。
#![allow(unused)]
fn main() {
trait T {
fn f1((a, b): (i32, i32)) {} // OK:是不可反驳的
}
}
#![allow(unused)]
fn main() {
trait T {
fn f1(123: i32) {} // 错误:模式是可反驳的
fn f2(Some(x): Option<i32>) {} // 错误:模式是可反驳的
}
}
2018 Edition differences
在 2018 版本之前,关联函数参数的模式是可选的:
#![allow(unused)] fn main() { // 2015 版本 trait T { fn f(i32); // OK:参数标识符不是必需的 } }从 2018 版本开始,模式不再是可选的。
2018 Edition differences
在 2018 版本之前,带主体的关联函数中的参数仅限于以下类型的模式:
- IDENTIFIER
mutIDENTIFIER_&IDENTIFIER&&IDENTIFIER#![allow(unused)] fn main() { // 2015 版本 trait T { fn f1((a, b): (i32, i32)) {} // 错误:不允许的模式 } }从 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 {
// 空的 `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();
}