derive
属性について。
derive
属性は型にトレイトを自動実装するのに使う。
これは内部的には手続きマクロを利用していて、フィールドの宣言情報などを読み、そこからよくある実装コードを自動で導出している。
std
での利用例
以下は std
における derive
属性に対応するトレイトの一覧である。それぞれのトレイトの概要と導出の動作についてまとめている。また、トレイトには他のトレイトを前提にするものもあり、それらについても触れている (例: Copy
は Clone
が前提)。
Debug
概要: 値の内容をデバッグ書式で出力する。
導出: 型名や各フィールドの値を出力する。
Default
概要: 既定値を生成する。
導出: 全てのフィールドを既定値で生成する。
Clone
概要: 元の値をクローンして新しい値を作る。
導出: 全てのフィールドの値をクローンする。
Copy: Clone
概要: 値のコピー (自動的なクローン) を可能にする。
導出: マーカートレイトのため宣言のみの導出となる。
Eq: PartialEq
概要: 二つの値の同値関係を判定する。
導出: マーカートレイトのため宣言のみの導出となる。
PartialEq
概要: 二つの値の半同値関係を判定する (==
や !=
演算子でも使用) 。
導出: 全てのフィールドの値が等しいかを判定する。
Ord: Eq, PartialOrd
概要: 二つの値の順序関係を判定する。
導出: マーカートレイトのため宣言のみの導出となる。
PartialOrd: PartialEq
概要: 二つの値の半順序関係を判定する (<
や >
演算子でも使用)。
導出: 全てのフィールドの値を宣言順に並べて辞書式で比較する。
Hash
概要: 値からハッシュ値を計算する。
導出: 全てのフィールドの値をハッシュに利用する。
以下では、Point
に Default
, 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
属性を無理に使うと以下のようなエラーが発生する。
derive
可能)
以下では MyType<T>
は Clone
と PartialEq
を自動実装している。
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
}
}
derive
不可)
以下では MyVec<T>
は Clone
と PartialEq
を自動実装している。
しかし、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())
}
}