bigywater.tech
Rust · 47

Trait 实现冲突与孤儿规则

孤儿规则和一致性是 Rust trait 系统里最「反直觉」的两条限制。这篇不背规则,而是把各种组合都编译一遍,让编译器自己说明:什么能写、什么不能写、为什么连「看起来没冲突」的实现也会被拒绝。

E0117
外部 trait + 外部类型的错误
E0119
实现重叠的错误,含「upstream crates may add a new impl」的提示
fundamental
Box 可以而 Vec 不行的原因(Box 是 #[fundamental])

孤儿规则

实测 · 本机执行输出
  外部 trait + 外部类型(Display for Vec<u8>)
      → 失败:error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
  本地 trait + 外部类型(MyTr for Vec<u8>)
      → 编译通过
  外部 trait + 本地类型(Display for Local)
      → 编译通过
  外部 trait + 外部类型,但 trait 的类型参数是本地类型(From<Local> for Vec<u8>)
      → 编译通过
  外部 trait + Box<本地类型>(Box 是 #[fundamental])
      → 编译通过
  外部 trait + Vec<本地类型>(Vec 不是 fundamental)
      → 失败:error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate

newtype 的做法见Newtype 模式。

一致性

实测 · 本机执行输出
  源码:
      1 | trait Describe { fn d(&self) -> String; }
      2 | impl<T: std::fmt::Display> Describe for T { fn d(&self) -> String { format!("显示:{}", self) } }
      3 | impl Describe for i32 { fn d(&self) -> String { "整数".into() } }
      4 | fn main() {}
  编译:失败
    error[E0119]: conflicting implementations of trait `Describe` for type `i32`
     --> <实验目录>/src/u47a.rs:3:1
      |
    2 | impl<T: std::fmt::Display> Describe for T { fn d(&self) -> String { format!("显示:{}", self) } }
      | ----------------------------------------- first implementation here
    3 | impl Describe for i32 { fn d(&self) -> String { "整数".into() } }
      | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `i32`
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0119`.

上游可能新增实现

实测 · 本机执行输出
  源码:
      1 | trait Describe { fn d(&self) -> String; }
      2 | impl<T: std::fmt::Display> Describe for T { fn d(&self) -> String { format!("显示:{}", self) } }
      3 | impl Describe for Vec<u8> { fn d(&self) -> String { "字节向量".into() } }
      4 | fn main() {}
  编译:失败
    error[E0119]: conflicting implementations of trait `Describe` for type `Vec<u8>`
     --> <实验目录>/src/u47b.rs:3:1
      |
    2 | impl<T: std::fmt::Display> Describe for T { fn d(&self) -> String { format!("显示:{}", self) } }
      | ----------------------------------------- first implementation here
    3 | impl Describe for Vec<u8> { fn d(&self) -> String { "字节向量".into() } }
      | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Vec<u8>`
      |
      = note: upstream crates may add a new impl of trait `std::fmt::Display` for type `std::vec::Vec<u8>` in future versions
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0119`.
  源码:
      1 | trait Describe { fn d(&self) -> String; }
      2 | impl<T: std::fmt::Display> Describe for T { fn d(&self) -> String { format!("显示:{}", self) } }
      3 | struct Mine;                                            // 本地类型:上游永远不会给它实现 Display
      4 | impl Describe for Mine { fn d(&self) -> String { "我的类型".into() } }
      5 | fn main() { println!("{} {}", Mine.d(), 5.d()); }
  编译:成功
  运行(退出码 0):
    我的类型 显示:5

blanket 实现的更多例子见Blanket Implementation。

绕开的办法

实测 · 本机执行输出
  源码:
      1 | use std::fmt;
      2 | struct Bytes(Vec<u8>);                                                      // newtype
      3 | impl fmt::Display for Bytes { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} 字节", self.0.len()) } }
      4 | trait Hex { fn hex(&self) -> String; }                                      // 自己的 trait,可以为外部类型实现
      5 | impl Hex for Vec<u8> { fn hex(&self) -> String { self.iter().map(|b| format!("{b:02x}")).collect() } }
      6 | fn main() { println!("{} / {}", Bytes(vec![1, 2, 3]), vec![0xde, 0xad].hex()); }
  编译:成功
  运行(退出码 0):
    3 字节 / dead

分步演示

孤儿规则
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。错误信息为原样的 rustc 1.98.1 输出
    • 没有验证:#[fundamental] 属性的完整列表(只测了 Box 与 Vec 的对比)、negative impls、specialization、多 crate 场景下的孤儿规则(这里只有单 crate)、依赖升级导致的一致性破坏
    京ICP备2026067502号-1京公网安备11010502063487号