关联项

语法
关联项 :
   外部属性* (
         宏调用分号
      | ( 可见性? ( 类型别名 | 常量项 | 函数 ) )
   )

关联项是在 trait 中声明或在 实现 中定义的项。之所以这样称呼它们,是因为它们是在关联类型上定义的——即实现中的类型。

它们是在模块中可以声明的项的种类的子集。具体来说,有 关联函数(包括方法)、关联类型关联常量

当关联项在逻辑上与关联项相关时,关联项非常有用。例如,Option 上的 is_some 方法本质上与 Option 相关,因此应该是关联的。

每种关联项类型都有两种变体:包含实际实现的定义,以及为定义声明签名的声明

正是这些声明构成了 trait 的契约以及在泛型类型上可用的内容。

关联函数和方法

关联函数是与类型关联的 函数

关联函数声明为关联函数定义声明签名。它的写法类似于函数项,只是函数体被替换为 ;

标识符是函数的名称。

关联函数的泛型、参数列表、返回类型和 where 子句必须与关联函数声明的相同。

关联函数定义定义了一个与另一种类型关联的函数。它的写法与 函数项 相同。

一个常见的关联函数的例子是 new 函数,它返回与关联函数关联的类型的值。

struct Struct {
    field: i32
}

impl Struct {
    fn new() -> Struct {
        Struct {
            field: 0i32
        }
    }
}

fn main () {
    let _struct = Struct::new();
}

当关联函数在 trait 上声明时,该函数也可以通过 路径 调用,该路径是指向 trait 的路径,后跟 trait 的名称。当发生这种情况时,它会被替换为 <_ as Trait>::function_name

#![allow(unused)]
fn main() {
trait Num {
    fn from_i32(n: i32) -> Self;
}

impl Num for f64 {
    fn from_i32(n: i32) -> f64 { n as f64 }
}

// These 4 are all equivalent in this case.
let _: f64 = Num::from_i32(42);
let _: f64 = <_ as Num>::from_i32(42);
let _: f64 = <f64 as Num>::from_i32(42);
let _: f64 = f64::from_i32(42);
}

方法

第一个参数名为 self 的关联函数称为方法,可以使用 方法调用运算符(例如,x.foo())以及常用的函数调用表示法来调用。

如果指定了 self 参数的类型,则它仅限于解析为以下语法生成的类型之一(其中 'lt 表示某些任意生命周期)

P = &'lt S | &'lt mut S | Box<S> | Rc<S> | Arc<S> | Pin<P>
S = Self | P

此语法中的 Self 终结符表示解析为实现类型的类型。这也可能包括上下文类型别名 Self、其他类型别名或解析为实现类型的关联类型投影。

#![allow(unused)]
fn main() {
use std::rc::Rc;
use std::sync::Arc;
use std::pin::Pin;
// Examples of methods implemented on struct `Example`.
struct Example;
type Alias = Example;
trait Trait { type Output; }
impl Trait for Example { type Output = Example; }
impl Example {
    fn by_value(self: Self) {}
    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 explicit_type(self: Arc<Example>) {}
    fn with_lifetime<'a>(self: &'a Self) {}
    fn nested<'a>(self: &mut &'a Arc<Rc<Box<Alias>>>) {}
    fn via_projection(self: <Example as Trait>::Output) {}
}
}

可以使用简写语法,而无需指定类型,它们具有以下等效形式

简写等效
selfself: Self
&'lifetime selfself: &'lifetime Self
&'lifetime mut selfself: &'lifetime mut Self

注意:生命周期可以(并且通常)使用此简写省略。

如果 self 参数以 mut 为前缀,它将变成一个可变变量,类似于使用 mut 标识符模式 的常规参数。例如

#![allow(unused)]
fn main() {
trait Changer: Sized {
    fn change(mut self) {}
    fn modify(mut self: Box<Self>) {}
}
}

作为 trait 上方法的示例,请考虑以下内容

#![allow(unused)]
fn main() {
type Surface = i32;
type BoundingBox = i32;
trait Shape {
    fn draw(&self, surface: Surface);
    fn bounding_box(&self) -> BoundingBox;
}
}

这定义了一个具有两个方法的 trait。所有具有此 trait 的 实现 并且 trait 在作用域内的值都可以调用它们的 drawbounding_box 方法。

#![allow(unused)]
fn main() {
type Surface = i32;
type BoundingBox = i32;
trait Shape {
    fn draw(&self, surface: Surface);
    fn bounding_box(&self) -> BoundingBox;
}

struct Circle {
    // ...
}

impl Shape for Circle {
    // ...
  fn draw(&self, _: Surface) {}
  fn bounding_box(&self) -> BoundingBox { 0i32 }
}

impl Circle {
    fn new() -> Circle { Circle{} }
}

let circle_shape = Circle::new();
let bounding_box = circle_shape.bounding_box();
}

版本差异:在 2015 版本中,可以声明带有匿名参数的 trait 方法(例如 fn foo(u8))。这已被弃用,并且在 2018 版本中是一个错误。所有参数都必须具有参数名称。

方法参数上的属性

方法参数上的属性遵循与 常规函数参数 相同的规则和限制。

关联类型

关联类型是与另一种类型关联的 类型别名

关联类型不能在 固有实现 中定义,也不能在 trait 中给出默认实现。

关联类型声明为关联类型定义声明签名。它以以下形式之一编写,其中 Assoc 是关联类型的名称,Params 是类型、生命周期或 const 参数的逗号分隔列表,Bounds 是关联类型必须满足的 trait bound 的加号分隔列表,WhereBounds 是参数必须满足的 bound 的逗号分隔列表

type Assoc;
type Assoc: Bounds;
type Assoc<Params>;
type Assoc<Params>: Bounds;
type Assoc<Params> where WhereBounds;
type Assoc<Params>: Bounds where WhereBounds;

标识符是声明的类型别名的名称。

可选的 trait bound 必须由类型别名的实现来满足。

关联类型上有一个隐式的 Sized bound,可以使用特殊的 ?Sized bound 来放宽它。

关联类型定义为类型上 trait 的实现定义类型别名

它们的写法类似于关联类型声明,但不能包含 Bounds,而是必须包含 Type

type Assoc = Type;
type Assoc<Params> = Type; // the type `Type` here may reference `Params`
type Assoc<Params> = Type where WhereBounds;
type Assoc<Params> where WhereBounds = Type; // deprecated, prefer the form above

如果类型 Item 具有来自 trait Trait 的关联类型 Assoc,则 <Item as Trait>::Assoc 是一种类型,它是关联类型定义中指定的类型的别名

此外,如果 Item 是类型参数,则 Item::Assoc 可以用于类型参数。

关联类型可能包含 泛型参数where 子句;这些通常被称为泛型关联类型GAT。如果类型 Thing 具有来自 trait Trait 的关联类型 Item,并且带有泛型 <'a>,则该类型可以命名为 <Thing as Trait>::Item<'x>,其中 'x 是作用域内的一些生命周期。在这种情况下,'x 将在 impl 上的关联类型定义中 'a 出现的地方使用。

trait AssociatedType {
    // Associated type declaration
    type Assoc;
}

struct Struct;

struct OtherStruct;

impl AssociatedType for Struct {
    // Associated type definition
    type Assoc = OtherStruct;
}

impl OtherStruct {
    fn new() -> OtherStruct {
        OtherStruct
    }
}

fn main() {
    // Usage of the associated type to refer to OtherStruct as <Struct as AssociatedType>::Assoc
    let _other_struct: OtherStruct = <Struct as AssociatedType>::Assoc::new();
}

带有泛型和 where 子句的关联类型的示例

struct ArrayLender<'a, T>(&'a mut [T; 16]);

trait Lend {
    // Generic associated type declaration
    type Lender<'a> where Self: 'a;
    fn lend<'a>(&'a mut self) -> Self::Lender<'a>;
}

impl<T> Lend for [T; 16] {
    // Generic associated type definition
    type Lender<'a> = ArrayLender<'a, T> where Self: 'a;

    fn lend<'a>(&'a mut self) -> Self::Lender<'a> {
        ArrayLender(self)
    }
}

fn borrow<'a, T: Lend>(array: &'a mut T) -> <T as Lend>::Lender<'a> {
    array.lend()
}

fn main() {
    let mut array = [0usize; 16];
    let lender = borrow(&mut array);
}

关联类型容器示例

考虑以下 Container trait 的示例。请注意,该类型可用于方法签名中

#![allow(unused)]
fn main() {
trait Container {
    type E;
    fn empty() -> Self;
    fn insert(&mut self, elem: Self::E);
}
}

为了使类型实现此 trait,它不仅必须为每个方法提供实现,还必须指定类型 E。这是标准库类型 VecContainer 实现

#![allow(unused)]
fn main() {
trait Container {
    type E;
    fn empty() -> Self;
    fn insert(&mut self, elem: Self::E);
}
impl<T> Container for Vec<T> {
    type E = T;
    fn empty() -> Vec<T> { Vec::new() }
    fn insert(&mut self, x: T) { self.push(x); }
}
}

BoundsWhereBounds 之间的关系

在这个例子中

#![allow(unused)]
fn main() {
use std::fmt::Debug;
trait Example {
    type Output<T>: Ord where T: Debug;
}
}

给定对关联类型的引用,例如 <X as Example>::Output<Y>,关联类型本身必须是 Ord,并且类型 Y 必须是 Debug

泛型关联类型上需要的 where 子句

trait 上的泛型关联类型声明目前可能需要一个 where 子句列表,具体取决于 trait 中的函数以及 GAT 的使用方式。这些规则将来可能会放宽;更新可以在 泛型关联类型倡议仓库 中找到。

简而言之,需要这些 where 子句是为了最大化 impl 中关联类型的允许定义。为此,在 GAT 作为输入或输出出现的函数(使用函数或 trait 的参数)上可以被证明成立的任何子句也必须写在 GAT 本身之上。

#![allow(unused)]
fn main() {
trait LendingIterator {
    type Item<'x> where Self: 'x;
    fn next<'a>(&'a mut self) -> Self::Item<'a>;
}
}

在上面,在 next 函数上,我们可以证明 Self: 'a,因为来自 &'a mut self 的隐式 bound;因此,我们必须在 GAT 本身写下等效的 bound:where Self: 'x

当 trait 中有多个函数使用 GAT 时,则使用来自不同函数的 bound 的交集,而不是并集。

#![allow(unused)]
fn main() {
trait Check<T> {
    type Checker<'x>;
    fn create_checker<'a>(item: &'a T) -> Self::Checker<'a>;
    fn do_check(checker: Self::Checker<'_>);
}
}

在这个例子中,type Checker<'a>; 上不需要 bound。虽然我们知道 create_checker 上的 T: 'a,但我们不知道 do_check 上的情况。但是,如果 do_check 被注释掉,则 Checker 上将需要 where T: 'x bound。

关联类型上的 bound 也会传播需要的 where 子句。

#![allow(unused)]
fn main() {
trait Iterable {
    type Item<'a> where Self: 'a;
    type Iterator<'a>: Iterator<Item = Self::Item<'a>> where Self: 'a;
    fn iter<'a>(&'a self) -> Self::Iterator<'a>;
}
}

在这里,由于 iterItem 上需要 where Self: 'a。但是,Item 用于 Iterator 的 bound 中,因此那里也需要 where Self: 'a 子句。

最后,trait 中 GAT 上对 'static 的任何显式使用都不会计入需要的 bound。

#![allow(unused)]
fn main() {
trait StaticReturn {
    type Y<'a>;
    fn foo(&self) -> Self::Y<'static>;
}
}

关联常量

关联常量是与类型关联的 常量

关联常量声明为关联常量定义声明签名。它的写法是 const,然后是标识符,然后是 :,然后是类型,最后以 ; 结尾。

标识符是路径中使用的常量的名称。类型是定义必须实现的类型。

关联常量定义定义了一个与类型关联的常量。它的写法与 常量项 相同。

关联常量定义仅在被引用时才进行 常量求值。此外,包含 泛型参数 的定义在单态化之后进行求值。

struct Struct;
struct GenericStruct<const ID: i32>;

impl Struct {
    // Definition not immediately evaluated
    const PANIC: () = panic!("compile-time panic");
}

impl<const ID: i32> GenericStruct<ID> {
    // Definition not immediately evaluated
    const NON_ZERO: () = if ID == 0 {
        panic!("contradiction")
    };
}

fn main() {
    // Referencing Struct::PANIC causes compilation error
    let _ = Struct::PANIC;

    // Fine, ID is not 0
    let _ = GenericStruct::<1>::NON_ZERO;

    // Compilation error from evaluating NON_ZERO with ID=0
    let _ = GenericStruct::<0>::NON_ZERO;
}

关联常量示例

一个基本示例

trait ConstantId {
    const ID: i32;
}

struct Struct;

impl ConstantId for Struct {
    const ID: i32 = 1;
}

fn main() {
    assert_eq!(1, Struct::ID);
}

使用默认值

trait ConstantIdDefault {
    const ID: i32 = 1;
}

struct Struct;
struct OtherStruct;

impl ConstantIdDefault for Struct {}

impl ConstantIdDefault for OtherStruct {
    const ID: i32 = 5;
}

fn main() {
    assert_eq!(1, Struct::ID);
    assert_eq!(5, OtherStruct::ID);
}