Progrust Library.

// dictionary

derive属性.

derive属性は、構造体や列挙型の定義に#[derive(Clone, Debug)]と書き添えるだけで、指定したトレイトの実装をコンパイラに自動生成させる仕組みです。フィールドを1つずつ辿るだけの定型的なimplブロックを、手で書かずに済ませられます。標準ライブラリとコンパイラが提供する組み込みの導出は9種類で、実体はマクロの一種であるderiveマクロです1。書けるのは構造体・列挙型・共用体(union)の定義に対してだけで、関数や型エイリアスに付けるとエラー: E0774になります2。

#[derive(Debug, Clone, PartialEq)]
struct Book {
    title: String,
    price: u32,
}

fn main() {
    let book = Book {
        title: String::from("Rust入門"),
        price: 3200,
    };
    let spare = book.clone(); // Clone: .clone()で複製できる
    println!("{:?}", spare); // Debug: {:?}で中身を表示できる
    println!("同じ本か: {}", book == spare); // PartialEq: ==で比較できる
}
Playgroundで開く

導出できるトレイト一覧

組み込みのderiveは次の9種類です1。

トレイト 自動生成される実装
Clone 全フィールドを.clone()した複製を返す
Copy ビット単位の複製でよいと宣言する(メソッドなし)
Debug {:?}で型名・フィールド名・値を並べて表示する
Default 全フィールドをその型の既定値で埋めた値を作る
PartialEq 全フィールドの比較で==・!=を定義する
Eq PartialEqに加えて同値関係が成り立つと宣言する(メソッドなし)3
PartialOrd 辞書式比較で<などを定義する。優先度は構造体ならフィールドの宣言順、列挙型ならバリアントの判別子(既定では宣言順)4
Ord 同じ順序づけを全順序として定義する
Hash 全フィールドをハッシュ値の計算に混ぜ込む

各トレイトの詳細はクローン・Copyトレイト・Debugトレイト・Defaultトレイト・比較のトレイト・コンソール出力の各項目で扱っています。

導出の条件(全フィールドの実装)

deriveが作るのは「各フィールドに同じ処理を委譲する」実装です。そのため対象の型の全フィールド(列挙型なら全バリアントの全フィールド)が、そのトレイトを実装している必要があります。1つでも欠けると自動生成に失敗し、コンパイルエラーになります。

struct Isbn(String); // Clone を実装していない

#[derive(Clone)] // エラー: E0277(Isbn が Clone を実装していない)
struct Book {
    isbn: Isbn,
    price: u32,
}

fn main() {
    let book = Book {
        isbn: Isbn(String::from("978-4-00-000000-0")),
        price: 3200,
    };
    println!("{}円", book.price);
}
Playgroundで開く

補足

Copyを導出するときの追加条件

トレイト側の要求も同時に満たす必要があります。CopyはCloneを継承しているため#[derive(Copy)]だけでは足りず(エラー: E0277)、#[derive(Copy, Clone)]とセットで書きます。加えてCopyには「Dropを実装していないこと」という条件もあります(エラー: E0184)。

ジェネリックな型に付けたとき

ジェネリクスを使った型にderiveを付けると、生成される実装には、すべての型引数に対して同じトレイトのトレイト境界が付きます。例えば#[derive(Clone)] struct Foo<T> { .. }からはimpl<T: Clone> Clone for Foo<T>が生成されます1。

この境界は、フィールドの型を見て必要最小限に絞り込まれるわけではありません。次のShared<T>はRc<T>しか持たず、Rc<T>はTが何であってもクローンできますが、生成された実装がT: Cloneを要求するため.clone()が呼べません。

use std::rc::Rc;

// impl<T: Clone> Clone for Shared<T> が生成される
#[derive(Clone)]
struct Shared<T> {
    inner: Rc<T>,
}

struct Config; // Clone を実装していない

fn main() {
    let shared = Shared { inner: Rc::new(Config) };
    let _spare = shared.clone(); // エラー: E0599(T: Clone を満たさない)
}
Playgroundで開く

このような型では、deriveをやめてimpl<T> Clone for Shared<T>を手で書きます。

列挙型にDefaultを導出するには

列挙型はどのバリアントを既定とすべきかをコンパイラが決められないため、#[derive(Default)]だけではエラー: E0665になります。フィールドを持たないバリアントに#[default]属性を付けて明示します(Rust 1.62以降)6。

#[derive(Debug, Default)]
enum Payment {
    #[default]
    Cash,
    Credit,
    QrCode,
}

fn main() {
    println!("{:?}", Payment::default()); // Cash
}
Playgroundで開く
外部クレートが提供するderive

組み込みの9種以外にも、外部パッケージが独自のderiveマクロを提供していることがあります(#[derive(Serialize)]など)。これらは手続き的マクロとして定義され、#[serde(rename = "...")]のようなヘルパー属性をフィールドやバリアントに付けて挙動を細かく指定できます7。ヘルパー属性はそれ自体では何もせず、宣言元のderiveマクロが読み取るための目印です。

Footnotes

  1. The Rust Reference — Derive “The derive attribute invokes one or more derive macros, allowing new items to be automatically generated for data structures.” / “The PartialEq derive macro emits an implementation of PartialEq for Foo<T> where T: PartialEq.” 組み込みderiveとしてClone・Copy・Debug・Default・Eq・Hash・Ord・PartialEq・PartialOrdの9つが列挙されている。 ↩ ↩2 ↩3

  2. The Rust Reference — Derive “The derive attribute may only be applied to structs, enums, and unions.”(エラーコードはRust 1.93で確認) ↩

  3. std公式ドキュメント — Trait Eq “When derived, because Eq has no extra methods, it is only informing the compiler that this is an equivalence relation rather than a partial equivalence relation.” ↩

  4. std公式ドキュメント — Trait PartialOrd “When derived on structs, it will produce a lexicographic ordering based on the top-to-bottom declaration order of the struct’s members. When derived on enums, variants are primarily ordered by their discriminants.” ↩

  5. std公式ドキュメント — Trait Display “Display is similar to Debug, but Display is for user-facing output, and so cannot be derived.” ↩

  6. std公式ドキュメント — Trait Default “When using #[derive(Default)] on an enum, you need to choose which unit variant will be default. You do this by placing the #[default] attribute on the variant.” ↩

  7. The Rust Reference — Derive macro helper attributes “Derive macros can declare derive macro helper attributes to be used within the scope of the item to which the derive macro is applied. These attributes are inert.” ↩

この辞書が使われているページ

backlinks 8

  1. 辞書Copyトレイト
  2. 辞書Debugトレイト
  3. 辞書Defaultトレイト
  4. 辞書Displayトレイト
  5. 辞書トレイト
  6. 辞書演算子オーバーロード
  7. 辞書比較のトレイト
  8. 本動かして学ぶRustプログラミング問題集 › 第19章 derive属性と標準トレイト