Keyword trait

source ·
Expand description

一组类型的通用接口。

trait 就像数据类型可以实现的接口。当一个类型要实现 trait 时,可以使用泛型或 trait 对象将其抽象地视为该 trait。

traits 可以由三个关联项组成:

  • 函数和方法
  • types
  • constants

traits 也可能包含额外的类型参数。这些类型参数或 trait 本身可以受到其他 traits 的约束。

traits 可以作为标记,也可以携带其他逻辑语义,这些逻辑语义不是通过其项表示的。 当一个类型实现该 trait 时,它就会承诺遵守它的契约。 SendSync 就是标准库中存在的两个这样的标记 traits。

有关 traits 的更多信息,请参见 Reference

Examples

traits 是使用 trait 关键字声明的。类型可以使用 impl Trait for Type 来实现它们:

trait Zero {
    const ZERO: Self;
    fn is_zero(&self) -> bool;
}

impl Zero for i32 {
    const ZERO: Self = 0;

    fn is_zero(&self) -> bool {
        *self == Self::ZERO
    }
}

assert_eq!(i32::ZERO, 0);
assert!(i32::ZERO.is_zero());
assert!(!4.is_zero());
Run

带有关联类型:

trait Builder {
    type Built;

    fn build(&self) -> Self::Built;
}
Run

traits 可以是泛型,可以有约束,也可以没有约束:

trait MaybeFrom<T> {
    fn maybe_from(value: T) -> Option<Self>
    where
        Self: Sized;
}
Run

traits 可以建立在其他 traits 的要求之上。在下面的示例中,Iterator 是一个 supertrait,而 ThreeIterator 是一个 subtrait

trait ThreeIterator: Iterator {
    fn next_three(&mut self) -> Option<[Self::Item; 3]>;
}
Run

traits 可以在函数中作为参数使用:

fn debug_iter<I: Iterator>(it: I) where I::Item: std::fmt::Debug {
    for elem in it {
        println!("{elem:#?}");
    }
}

// u8_len_1,u8_len_2 和 u8_len_3 是等效的

fn u8_len_1(val: impl Into<Vec<u8>>) -> usize {
    val.into().len()
}

fn u8_len_2<T: Into<Vec<u8>>>(val: T) -> usize {
    val.into().len()
}

fn u8_len_3<T>(val: T) -> usize
where
    T: Into<Vec<u8>>,
{
    val.into().len()
}
Run

或作为返回类型:

fn from_zero_to(v: u8) -> impl Iterator<Item = u8> {
    (0..v).into_iter()
}
Run

在该位置使用 impl 关键字,函数 writer 可以将具体类型隐藏为实现细节,可以在不破坏用户代码的情况下对其进行更改。

trait 对象

trait 对象 是实现一组 traits 的另一种类型的不透明值。一个 trait 对象实现了所有指定的 traits,以及它的 supertraits (如果有的话)。

语法如下: dyn BaseTrait + AutoTrait1 + ... AutoTraitN。 只能使用一个 BaseTrait,因此无法编译:

trait A {}
trait B {}

let _: Box<dyn A + B>;
Run

这也不能,这是一个语法错误:

trait A {}
trait B {}

let _: Box<dyn A + dyn B>;
Run

另一方面,这是正确的:

trait A {}

let _: Box<dyn A + Send + Sync>;
Run

Reference 中记录了更多关于 trait 对象的信息,以及它们的局限性和版本之间的差异。

不安全的 traits

某些 traits 可能无法安全的实现。在 trait 声明的前面,使用 unsafe 关键字进行标记:

unsafe trait UnsafeTrait {}

unsafe impl UnsafeTrait for i32 {}
Run

2015 年版和 2018 年版之间的差异

在 2015 版中,traits 不需要参数模式:

trait Tr {
    fn f(i32);
}
Run

此行为在 2018 版中不再有效。