特征

语法

特征 :

   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 类型上声明的,并传递性地声明在这些特征边界中声明的特征的超级特征。如果一个特征是它自己的超级特征,则会出现错误。

具有超级特征的特征称为其超级特征的子特征

以下示例声明 ShapeCircle 的超级特征。

#![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 是不安全特征的示例。

参数模式

没有主体的函数或方法声明只允许 标识符_ 通配符 模式。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();
}