bigywater.tech
Rust · 11

derive 自动生成 Trait 实现

derive 是 Rust 里最常写的一行属性。这篇不只讲「它能做什么」,而是把它展开:没有它编译器怎么报错、有了它输出什么、它到底生成了哪些 impl 代码、哪些情况下它不能用、对泛型它悄悄加了什么约束。

E0277
没有 derive(Debug) 就打印时的错误
展开
-Z unpretty=expanded 可以看到 derive 生成的 impl 代码
T: Trait
泛型结构体上 derive 自动加的约束

没有 derive

实测 · 本机执行输出
  源码:
      1 | struct P { x: i32, y: i32 }
      2 | fn main() {
      3 |     let a = P { x: 1, y: 2 };
      4 |     println!("{:?}", a);
      5 |     let b = a.clone();
      6 |     println!("{}", a == b);
      7 | }
  编译:失败
    error[E0277]: `P` doesn't implement `Debug`
     --> <实验目录>/src/u11a.rs:4:22
      |
    4 |     println!("{:?}", a);
      |               ----   ^ `P` cannot be formatted using `{:?}` because it doesn't implement `Debug`
      |               |
      |               required by this formatting parameter
      |
      = help: the trait `Debug` is not implemented for `P`
    help: consider annotating `P` with `#[derive(Debug)]`
      |
    1 + #[derive(Debug)]
    2 | struct P { x: i32, y: i32 }
      |
    
    error[E0599]: no method named `clone` found for struct `P` in the current scope
     --> <实验目录>/src/u11a.rs:5:15
      |
    1 | struct P { x: i32, y: i32 }
      | -------- method `clone` not found for this struct
    ...
    5 |     let b = a.clone();
      |               ^^^^^ method not found in `P`
      |
      = help: items from traits can only be used if the trait is implemented and in scope
      = note: the following trait defines an item `clone`, perhaps you need to implement it:
              candidate #1: `Clone`
    
    error: aborting due to 2 previous errors
    
    Some errors have detailed explanations: E0277, E0599.
    For more information about an error, try `rustc --explain E0277`.

加上 derive

实测 · 本机执行输出
  源码:
      1 | #[derive(Debug, Clone, PartialEq, Default)]
      2 | struct P { x: i32, y: i32, name: String }
      3 | fn main() {
      4 |     let a = P { x: 1, y: 2, name: "a".into() };
      5 |     let b = a.clone();
      6 |     println!("{:?}", a);
      7 |     println!("{:#?}", b);
      8 |     println!("a == b : {}", a == b);
      9 |     println!("Default:{:?}", P::default());
     10 | }
  编译:成功
  运行(退出码 0):
    P { x: 1, y: 2, name: "a" }
    P {
        x: 1,
        y: 2,
        name: "a",
    }
    a == b : true
    Default:P { x: 0, y: 0, name: "" }

derive 生成的代码

实测 · 本机执行输出
  #[prelude_import]
  struct P {
      x: i32,
      y: i32,
  }
  #[automatically_derived]
  impl ::core::fmt::Debug for P {
      #[inline]
      fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
          ::core::fmt::Formatter::debug_struct_field2_finish(f, "P", "x",
              &self.x, "y", &&self.y)
      }
  }
  #[automatically_derived]
  impl ::core::clone::Clone for P {
      #[inline]
      fn clone(&self) -> P {
          P {
              x: ::core::clone::Clone::clone(&self.x),
              y: ::core::clone::Clone::clone(&self.y),
          }
      }
  }
  #[automatically_derived]
  impl ::core::marker::StructuralPartialEq for P { }
  #[automatically_derived]
  impl ::core::cmp::PartialEq for P {
      #[inline]
      fn eq(&self, other: &P) -> bool { self.x == other.x && self.y == other.y }
  }
  fn main() {}

展开用的是 RUSTC_BOOTSTRAP=1 rustc -Z unpretty=expanded:在稳定版编译器上打开了只有 nightly 才有的 -Z 选项,仅用于观察。过程宏的执行阶段见过程宏的执行阶段,derive 宏的实现见Derive 宏如何生成代码。

Copy 与 PartialOrd

实测 · 本机执行输出
  源码:
      1 | #[derive(Clone, Copy)]
      2 | struct S { name: String }
      3 | fn main() {}
  编译:失败
    error[E0204]: the trait `Copy` cannot be implemented for this type
     --> <实验目录>/src/u11d.rs:2:8
      |
    1 | #[derive(Clone, Copy)]
      |                 ---- in this derive macro expansion
    2 | struct S { name: String }
      |        ^   ------------ this field does not implement `Copy`
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0204`.
  源码:
      1 | #[derive(Debug, PartialEq, PartialOrd)]
      2 | struct Ver { major: u32, minor: u32 }
      3 | #[derive(Debug, PartialEq, PartialOrd)]
      4 | enum Level { Low, Mid, High }
      5 | fn main() {
      6 |     println!("{}", Ver { major: 1, minor: 9 } < Ver { major: 2, minor: 0 });
      7 |     println!("{}", Ver { major: 2, minor: 1 } < Ver { major: 2, minor: 0 });
      8 |     println!("{}", Level::Low < Level::High);
      9 |     println!("{:?}", Level::Mid.partial_cmp(&Level::Low));
     10 | }
  编译:成功
  运行(退出码 0):
    true
    false
    true
    Some(Greater)

泛型上的 derive

实测 · 本机执行输出
  源码:
      1 | #[derive(Debug, Clone)]
      2 | struct W<T> { v: T }
      3 | struct NoClone;
      4 | fn main() {
      5 |     let a = W { v: 5 }; let b = a.clone(); println!("{:?}", b);
      6 |     let c = W { v: NoClone };
      7 |     let d = c.clone();
      8 | }
  编译:失败
    error[E0599]: the method `clone` exists for struct `W<NoClone>`, but its trait bounds were not satisfied
     --> <实验目录>/src/u11f.rs:7:15
      |
    2 | struct W<T> { v: T }
      | ----------- method `clone` not found for this struct because it doesn't satisfy `W<NoClone>: Clone`
    3 | struct NoClone;
      | -------------- doesn't satisfy `NoClone: Clone`
    ...
    7 |     let d = c.clone();
      |               ^^^^^ method cannot be called on `W<NoClone>` due to unsatisfied trait bounds
      |
    note: trait bound `NoClone: Clone` was not satisfied
     --> <实验目录>/src/u11f.rs:2:10
      |
    1 | #[derive(Debug, Clone)]
      |                 ----- in this derive macro expansion
    2 | struct W<T> { v: T }
      |          ^ type parameter would need to implement `Clone`
      = help: consider manually implementing the trait to avoid undesired bounds
    help: consider annotating `NoClone` with `#[derive(Clone)]`
      |
    3 + #[derive(Clone)]
    4 | struct NoClone;
      |
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0599`.

分步演示

derive 展开
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。展开输出省略了 prelude 导入等固定行;错误信息为原样的 rustc 1.98.1 输出
    • 没有验证:Hash 与 Eq 的派生、Ord 与 PartialOrd 的一致性、derive 在 enum 上的展开(只展开了 struct)、手写 impl 与 derive 的性能差别
    京ICP备2026067502号-1京公网安备11010502063487号