特质

语法
特质 :
   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) 意味着这一点)。

注意

此概念以前称为 对象安全

#![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 关键字开头。

SyncSend 是不安全特质的示例。

参数模式

没有函数体的函数或方法声明只允许 IDENTIFIER_ 通配符模式。当前允许 mut IDENTIFIER,但已弃用,将来会成为硬错误。

在 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.
}
}

项可见性

特质项在语法上允许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();
}