derive 属性について。

基礎知識

derive 属性は型にトレイトを自動実装するのに使う。

これは内部的には手続きマクロを利用していて、フィールドの宣言情報などを読み、そこからよくある実装コードを自動で導出している。

std での利用例

以下は std における derive 属性に対応するトレイトの一覧である。それぞれのトレイトの概要と導出の動作についてまとめている。また、トレイトには他のトレイトを前提にするものもあり、それらについても触れている (例: CopyClone が前提)。

Debug

概要: 値の内容をデバッグ書式で出力する。
導出: 型名や各フィールドの値を出力する。

Default

概要: 既定値を生成する。
導出: 全てのフィールドを既定値で生成する。

Clone

概要: 元の値をクローンして新しい値を作る。
導出: 全てのフィールドの値をクローンする。

Copy: Clone

概要: 値のコピー (自動的なクローン) を可能にする。
導出: マーカートレイトのため宣言のみの導出となる。

Eq: PartialEq

概要: 二つの値の同値関係を判定する。
導出: マーカートレイトのため宣言のみの導出となる。

PartialEq

概要: 二つの値の半同値関係を判定する (==!= 演算子でも使用) 。
導出: 全てのフィールドの値が等しいかを判定する。

Ord: Eq, PartialOrd

概要: 二つの値の順序関係を判定する。
導出: マーカートレイトのため宣言のみの導出となる。

PartialOrd: PartialEq

概要: 二つの値の半順序関係を判定する (<> 演算子でも使用)。
導出: 全てのフィールドの値を宣言順に並べて辞書式で比較する。

Hash

概要: 値からハッシュ値を計算する。
導出: 全てのフィールドの値をハッシュに利用する。

サンプル

以下では、PointDefault, Clone, Copy, Debug を実装している。


fn main() {
    let o = Point::default();
    let mut p = o; // copy.
    p.x = 3;
    p.y = 4;
    assert_eq!(format!("{o:?}"), "Point { x: 0, y: 0 }");
    assert_eq!(format!("{p:?}"), "Point { x: 3, y: 4 }");
    assert_eq!(p.abs(), 5.0);
}

#[derive(Default, Clone, Copy, Debug)]
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    pub fn abs(&self) -> f32 {
        ((self.x * self.x + self.y * self.y) as f32).sqrt()
    }
}

これは以下のコードと等しい。


fn main() {
    let o = Point::default();
    let mut p = o; // copy.
    p.x = 3;
    p.y = 4;
    assert_eq!(format!("{o:?}"), "Point { x: 0, y: 0 }");
    assert_eq!(format!("{p:?}"), "Point { x: 3, y: 4 }");
    assert_eq!(p.abs(), 5.0);
}

struct Point {
    x: i32,
    y: i32,
}

impl Point {
    pub fn abs(&self) -> f32 {
        ((self.x * self.x + self.y * self.y) as f32).sqrt()
    }
}

impl Default for Point {
    fn default() -> Self {
        Self {
            x: Default::default(),
            y: Default::default(),
        }
    }
}

impl Clone for Point {
    fn clone(&self) -> Self {
        Self {
            x: self.x.clone(),
            y: self.y.clone(),
        }
    }
}

impl Copy for Point {}

impl std::fmt::Debug for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Point")
            .field("x", &self.x)
            .field("y", &self.y)
            .finish()
    }
}

型パラメタの影響

型が型パラメタを持つ場合でも、derive 属性を使える。

ただしこの場合、型パラメタを対象のトレイトで境界づけるトレイトが多い。

つまり、#[derive(SomeTrait)] SomeType<T> から導出される実装は、T: SomeTrait の制限を含む場合が多く、この制限が妥当でない限り derive 属性は使えない。

関連エラー

derive 属性を無理に使うと以下のようなエラーが発生する。

サンプル 1 (derive 可能)

以下では MyType<T>ClonePartialEq を自動実装している。


fn main() {
    let var1 = MyType(42);
    let var2 = var1.clone();
    assert!(var1 == var2);
}

#[derive(Clone, PartialEq)]
struct MyType<T>(T);

これは以下のコードと等しい。


fn main() {
    let var1 = MyType(42);
    let var2 = var1.clone();
    assert!(var1 == var2);
}

struct MyType<T>(T);

impl<T: Clone> Clone for MyType<T> {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl<T: Eq> PartialEq for MyType<T> {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

サンプル 2 (derive 不可)

以下では MyVec<T>ClonePartialEq を自動実装している。
しかし、Default については独自にコードを書いて実装している。


fn main() {
    let values = values(1, 3);
    assert_eq!(values.0, vec![1, 1, 1]);
}

fn values<T: Clone>(value: T, len: usize) -> MyVec<T> {
    let mut ret = MyVec::default();
    for _ in 0..len {
        ret.0.push(value.clone());
    }

    ret
}

#[derive(Clone, PartialEq)]
struct MyVec<T>(Vec<T>);

impl<T> Default for MyVec<T> {
    fn default() -> Self {
        Self(Default::default())
    }
}