关联条目

语法
关联条目 :
   外围属性* (
         宏调用语句
      | ( 可见性? ( 类型别名 | 常量条目 | 函数 ) )
   )

关联条目traits实现 中类型定义的条目。 之所以称为关联条目,是因为是在关联类型中定义。 关联条目是可在模块中声明条目的子集,包括 关联函数 (包括方法)、 关联类型关联常量

关联条目通常与关联类型有逻辑关系。 例如, 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 名称的路径。 此时,会替代为 <_ 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 }
}

// 在这种情况下,这 4 个都是等价的。
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;
// 在结构 `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 版中,可以使用匿名参数 (例如 fn foo(u8) ) 声明 trait 方法。但在 2018 版中,已被弃用且会导致错误,所有参数必须要有参数名。

方法参数的属性

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

关联类型

关联类型 是与另一个类型相关联的 类型别名 。 关联类型不能在 内部实现 中定义,不能在 trait 中给出默认实现。

关联类型声明 是签名。 用以下一种形式书写,其中 Assoc 是关联类型的名称,Params 是由逗号分隔的类型、生命周期或常量参数列表, Bounds 是关联类型必须满足的一组用加号分隔的 trait 约束列表, WhereBounds 是参数必须满足的一组用逗号分隔的约束列表:

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

该标识符是已声明的类型别名的名称。可选的 trait 约束必须由类型别名的实现满足。 关联类型存在一个隐式 Sized 约束,可以使用特殊的 ?Sized 约束放宽。

关联类型定义 为类型别名。它们的书写方式与 关联类型声明 相似,而不能包含 Bounds ,必须包含一个 Type :

type Assoc = Type;
type Assoc<Params> = Type; // 这里的 `Type` 可以引用 `Params` 
type Assoc<Params> = Type where WhereBounds;
type Assoc<Params> where WhereBounds = Type; // 已废弃,请选择上面的形式

如果 Item 类型拥有一个从 Trait 中获取的关联类型 Assoc,即 <Item as Trait>::Assoc 。此外,如果 Item 是类型参数,那么类型参数中可以使用 Item::Assoc

关联类型可能包含 泛型参数where 约束 ;这些通常被称为 泛型关联类型 或 GAT。 如果类型 Thing 有一个来自 Trait 的关联类型 Item ,具有泛型 <'a> ,则可以像 <Thing as Trait> ::Item<'x> 这样命名该类型,其中 'x 是某个有效作用域内的生命周期。 此时,'x 将在实现中的关联类型定义中的 'a 处使用。

trait AssociatedType {
    // 关联类型声明
    type Assoc;
}

struct Struct;

struct OtherStruct;

impl AssociatedType for Struct {
    // 关联类型定义
    type Assoc = OtherStruct;
}

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

fn main() {
    // 用关联类型来指代 OtherStruct 为 <Struct as AssociatedType>::Assoc
    let _other_struct: OtherStruct = <Struct as AssociatedType>::Assoc::new();
}

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

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

trait Lend {
    // 泛型关联类型声明
    type Lender<'a> where Self: 'a;
    fn lend<'a>(&'a mut self) -> Self::Lender<'a>;
}

impl<T> Lend for [T; 16] {
    // 泛型关联类型定义
    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 从句是必需的,以便最大限度允许在 impls 中定义关联类型的范围。 为了做到这一点,在任何出现 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 推导出了这个隐含的约束。 因此,我们必须在 GAT 自身上写出等效的约束: where Self: 'x

当 trait 中有多个函数使用 GAT 时,会使用不同函数的约束的 交集 ,而不是并集。

#![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>; 不需要任何约束。虽然我们知道在 create_checker 函数中 T: 'a ,但是我们不知道在 do_check 函数中。 然而,如果将 do_check 函数注释掉,那么 Checker 就需要一个 where T: 'x 的约束。

关联类型上的约束也会传播所需的 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 。但是,由于 Iterator 的约束中使用了 Item ,因此也需要在那里写出 where Self: 'a 从句。

最后,在 trait 中对 GAT 进行显式使用时的 'static 不计入所需的约束。

#![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 {
    // 定义没有立即求值
    const PANIC: () = panic!("compile-time panic");
}

impl<const ID: i32> GenericStruct<ID> {
    // 定义没有立即求值
    const NON_ZERO: () = if ID == 0 {
        panic!("contradiction")
    };
}

fn main() {
    // 引用 Struct::PANIC 会导致编译错误
    let _ = Struct::PANIC;

    // 可以, ID 不是 0
    let _ = GenericStruct::<1>::NON_ZERO;

    // 在 ID=0 的情况下求值 NON_ZERO 是编译错误
    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`
trait ConstantIdDefault {
    // 声明一个常量 `ID`,默认值为 `1`
    const ID: i32 = 1;
}


struct Struct;

struct OtherStruct;

// 为结构体 `Struct` 实现 `ConstantIdDefault` trait
impl ConstantIdDefault for Struct {}

// 为结构体 `OtherStruct` 实现 `ConstantIdDefault` trait,并重新定义 `ID` 常量的值为 `5`
impl ConstantIdDefault for OtherStruct {
    const ID: i32 = 5;
}


fn main() {
    // 断言结构体 `Struct` 的 `ID` 常量值为 `1`
    assert_eq!(1, Struct::ID);
    // 断言结构体 `OtherStruct` 的 `ID` 常量值为 `5`
    assert_eq!(5, OtherStruct::ID);
}