< 返回我的博客

爱国的张浩予 发表于 2021-02-26 09:53

Tags:trait,implementation,type

初识 impl Trait for Type {...},我自以为 Trait 与实现类 Type 是“自由选择”的(戏称之:脚本思维后遗症)。其实,在给定 crate 内,什么 Type 能实现什么 Trait 还是有几条基本的“戒律”要守的。总结来就两条:

  • 孤儿原则
  • Trait 不重叠原则

若不能同时满足这两条原则,rustc 会认定【Trait 实现不一致】而抛出编译错误。咱们从最容易理解的原则讲起。

Trait 不重叠原则】

补充说明:这条限制,从RFC 1210(大约 2020 年)之后,就已经被去掉了。

若同一个 Type 同时实现多个 Trait(,当然就会同时存在多个 Trait 实现块),那么这多个 Trait 声明块内不能包含完全相同的:

  • 成员方法-签名
  • 关联函数-签名
  • 关联常量
  • 关联类型别名

这个好理解,咱们不能让同一个签名有多套不一样实现,不是?这会让 rustc 无所适从的。

【孤儿原则】

依据【Trait实现块】采用的语法不同,对【孤儿原则】的解释也小有差异:

  • 形式一:impl Trait for T0 {...}
    • TraitT0 之一必须是在当前 crate 内被定义。它们不能同时都是来自于第三方 crate
    • 其实,【形式一 】是【形式二】的一款简化特例。
  • 形式二:impl<P1..Pm> Trait<T1.., Ti, .., Tn> for T0 {...}
    • 要么,Trait 是在当前 crate 内被定义。
    • 要么,同时满足下面两个条件
      • TiT0..=Tn 列表里第一个在当前 crate 内定义的具体类型。
        • T0 自身是否来自于第三方 crate 并不重要,只要它是·具体类型·,就行。
      • P1..=Pm 最多只能出现在 (Ti, Tn] 的左开右闭区间(Rust 没有这类区间的表达符号)内,而不能落在 T0..Ti 左闭右开区间内。

【孤儿原则的例外

impl Trait for Box<Type> {...} 虽然 Box 定义是在标准库里,但只要 Type 是在当前 crate 内被定义,此·Trait 实现·即被认为是·一致的·。即,Box<Type> 就能实现 Trait

【后记】

  • P1..=Pm 是【实现块】的【类型参数】。术语:Uncovered 类型参数。
  • T1..=Tn 是被实现 Trait 自己的【类型参数】。
  • ..=Rust 对【闭区间】的表示语法。
  • for 关键字之前是应该有 where 从句以标注【类型参数】的【限定条件】的。但,这里为了简单将其省略了。

评论区

写评论
作者 爱国的张浩予 2021-02-28 12:03

感谢学友的热心回复,我上 Google 仔细查阅了一下从RFC 1210 开始,rustc 放宽了对【Trait Implementation Coherence】的限制和去掉了【overlapping implementation instances】限制,从而让 Rust 更符合人类工程学。大约在 2020 年这个 RFC 完全落地。

嗯。我的知识与经验有点落后了。

--
👇
viruscamp: 下面代码的 impl X for T1 的 fn f(&mut self) impl Y for T1 的 fn f(&mut self) 算不算是 “多个 Trait 声明块内不能包含完全相同的 成员方法-签名”? 但下面的代码是合法的。 要是我理解错了,你最好能给一个非法的例子。

trait X {
    fn f(&mut self);
    fn fx(&mut self);
}

trait Y {
    fn f(&mut self);
}

struct T1;
impl T1 {
    fn f1(&mut self) {
    }
}
impl X for T1 {
    fn f(&mut self) {
    }
    fn fx(&mut self) {
    }
}
impl Y for T1 {
    fn f(&mut self) {
    }
}

fn main() {
    let mut t1 = T1;
    t1.f1();
    t1.fx();
    //t1.f(); // multiple applicable items in scope
    X::f(&mut t1);
    Y::f(&mut t1);
}
viruscamp 2021-02-27 21:12

下面代码的 impl X for T1 的 fn f(&mut self) impl Y for T1 的 fn f(&mut self) 算不算是 “多个 Trait 声明块内不能包含完全相同的 成员方法-签名”? 但下面的代码是合法的。 要是我理解错了,你最好能给一个非法的例子。

trait X {
    fn f(&mut self);
    fn fx(&mut self);
}

trait Y {
    fn f(&mut self);
}

struct T1;
impl T1 {
    fn f1(&mut self) {
    }
}
impl X for T1 {
    fn f(&mut self) {
    }
    fn fx(&mut self) {
    }
}
impl Y for T1 {
    fn f(&mut self) {
    }
}

fn main() {
    let mut t1 = T1;
    t1.f1();
    t1.fx();
    //t1.f(); // multiple applicable items in scope
    X::f(&mut t1);
    Y::f(&mut t1);
}
1 共 2 条评论, 1 页