はじめに
こんにちは。細々とプログラミングをしているsotanengelです。
この記事は以下の記事の連載です。
他の連載記事 (詳細)
また本記事はEffective Rust(David Drysdale (著), 中田 秀基 (翻訳))を参考に作成されております。とてもいい書籍ですので興味を持った方は、ぜひ読んでみてください!
今日の内容
概要
Rustに標準で実装されているトレイトを使いこなすことで、
簡潔に必要な機能を実装しましょう。
Cloneを適切に実装しよう。
問(リンク)
Cloneを使うことである変数の内容をコピーしましょう。
またどのような場合にコピーすべきでないか考えてみましょう。
コード (詳細)
// TODO: Cloneを実装して、main関数が動くようにしましょう。
#[derive(Debug)]
struct User {
name: String,
age: u32,
}
// TODO: この構造体がCloneを実装すべきかどうか考えてみましょう。
#[derive(Debug)]
struct SecretKey {
key: Vec<u8>,
}
fn main() {
let user1 = User {
name: String::from("Alice"),
age: 30,
};
let user2 = user1;
println!("{:?}", user1);
println!("{:?}", user2);
let secret_key = SecretKey {
key: vec![1, 2, 3, 4, 5],
};
println!("{:?}", secret_key);
}
解答(リンク)
deriveを使えば簡単にCloneを実装できます。
また情報の管理を厳格にすべき構造体などについてはCloneの実装をよく検討すべきです。
コード (詳細)
#[derive(Debug, Clone)]
#[allow(dead_code)]
struct User {
name: String,
age: u32,
}
// 秘密鍵を保持する構造体
// 意図しない場所でコピーを作成して、カギの流出を防ぐためにCloneトレイトは実装しない方が良い
#[derive(Debug)]
#[allow(dead_code)]
struct SecretKey {
key: Vec<u8>,
}
fn main() {
let user1 = User {
name: String::from("Alice"),
age: 30,
};
let user2 = user1.clone();
println!("{:?}", user1);
println!("{:?}", user2);
let secret_key = SecretKey {
key: vec![1, 2, 3, 4, 5],
};
println!("{:?}", secret_key);
}
Copyを実装しましょう。
問(リンク)
PointにCopyを実装しましょう。
コード (詳細)
// TODO: この構造体にCopyを実装しましょう。
#[derive(Debug)]
#[allow(dead_code)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 10, y: 20 };
let p2 = p1; // p1の値がp2にコピーされる(ムーブではなくコピー)
// TODO: Point構造体がCopyを実装した時に、以下のコードはどのように書き直すべきでしょうか?
let p3 = p1.clone();
println!("p1: {:?}", p1);
println!("p2: {:?}", p2);
println!("p2: {:?}", p3);
}
解答(リンク)
コード参照。
コード (詳細)
#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 10, y: 20 };
let p2 = p1; // p1の値がp2にコピーされる(ムーブではなくコピー)
// CopyトレイトはCloneトレイトに制約を持つのでcloneを使うことができるが、
// ビット単位のコピーを作成する方が方が速いため、そのまま記載した方が良い。
let p3 = p1;
println!("p1: {:?}", p1);
println!("p2: {:?}", p2);
println!("p2: {:?}", p3);
}
Defaultを実装しよう
問(リンク)
構造体にDefaultを実装して、インスタンスの作成を簡潔に記載できるようにしましょう。
コード (詳細)
// TODO: この構造体にDefaultを実装しましょう。
#[derive(Debug)]
#[allow(dead_code)]
struct Point {
x: i32,
y: i32,
z: i32,
}
// TODO: このenumにDefaultを実装しましょう。
#[derive(Debug)]
#[allow(dead_code)]
enum Mode {
Automatic, // TODO: デフォルト値はこの要素にしてください。
Manual,
}
fn main() {
let p1 = Point::default();
println!("{:?}", p1);
// TODO: xには10を代入して、それ以外はデフォルト値を入れましょう。
let p2 = Point {};
println!("{:?}", p2);
let mode = Mode::default();
println!("{:?}", mode);
}
解答(リンク)
コード参照。
コード (詳細)
#[derive(Debug, Default)]
// i32がDefaultを実装しているため、この記載だけでデフォルト値が設定できます。
#[allow(dead_code)]
struct Point {
x: i32,
y: i32,
z: i32,
}
#[derive(Debug, Default)]
#[allow(dead_code)]
enum Mode {
#[default]
Automatic, // デフォルト値にしたい要素の前に記載をすることで簡単に定義ができます。
Manual,
}
fn main() {
let p1 = Point::default();
println!("{:?}", p1); // Point { x: 0, y: 0 , z:0 }
let p2 = Point {
x: 10,
..Default::default() // デフォルトを実装することで、フィールドの変更したい部分だけを明示的に書くことで初期化が可能になります。
};
println!("{:?}", p2); // Point { x: 10, y: 0 , z:0 }
let mode = Mode::default();
println!("{:?}", mode); // Automatic
}
PartialEqを実装して、比較ができるようにしましょう
問(リンク)
User構造体を比較できるようにしましょう。
また、NaNの混入が考えられるMeasurement構造体については自分でPartialEqを実装してみましょう。
コード (詳細)
// TODO: この構造体にPartialEqとEqを実装しましょう。
#[derive(Debug)]
struct User {
id: u32,
name: String,
}
// TODO: valueがNaN同士の比較であればtrueを返すように自分でPartialEqを実装しましょう。
#[derive(Debug)]
struct Measurement {
value: f64,
unit: String,
}
fn main() {
let u1 = User {
id: 1,
name: String::from("Alice"),
};
let u2 = User {
id: 1,
name: String::from("Alice"),
};
let u3 = User {
id: 2,
name: String::from("Bob"),
};
println!("u1 == u2: {}", u1 == u2); // true
println!("u1 == u3: {}", u1 == u3); // false
let m1 = Measurement {
value: f64::NAN,
unit: String::from("m"),
};
let m2 = Measurement {
value: f64::NAN,
unit: String::from("m"),
};
println!("m1 == m2: {}", m1 == m2); // true
}
解答(リンク)
コード参照。
コード (詳細)
#[derive(Debug, PartialEq, Eq)]
struct User {
id: u32,
name: String,
}
#[derive(Debug)]
struct Measurement {
value: f64,
unit: String,
}
// PartialEqの実装
impl PartialEq for Measurement {
fn eq(&self, other: &Self) -> bool {
(self.value.is_nan() && other.value.is_nan()) // 両方がNaNならtrue
|| (self.value == other.value) // それ以外は通常の比較
&& (self.unit == other.unit) // 単位も比較
}
}
fn main() {
let u1 = User {
id: 1,
name: String::from("Alice"),
};
let u2 = User {
id: 1,
name: String::from("Alice"),
};
let u3 = User {
id: 2,
name: String::from("Bob"),
};
println!("u1 == u2: {}", u1 == u2); // true
println!("u1 == u3: {}", u1 == u3); // false
let m1 = Measurement {
value: f64::NAN,
unit: String::from("m"),
};
let m2 = Measurement {
value: f64::NAN,
unit: String::from("m"),
};
println!("m1 == m2: {}", m1 == m2); // true
}
さいごに
もしも本リポジトリで不備などあれば、リポジトリのissueやPRなどでご指摘いただければと思います。