項目20:過剰な最適化の誘惑を退けよう
https://effective-rust.com/optimize.html
LT;DR
Rustacean は 参照 を使ってデータを効率的に共有し、所有 やコピーを避ける傾向にある
しかし、まずは利便性・保守性に焦点を当てて パフォーマンスが本当に重要な場合にだけ、最適化するようにしよう
Rust は基本的に ムーブセマンティクス だが、Copy トレイトを実装している型は コピーセマンティクス を使用する
Rc や Arc を用いると、値を複数箇所で共有して所有できる
加えてミュータブルな操作が必要となる場合は、内部可変性 を実現する RefCell や Mutex を組み合わせると良い
e.g. RefCell<Rc<T>>、Mutex<Arc<T>>
warning.icon デッドロック に注意が必要
hr.icon
データ構造とメモリ確保
参照 を用いると、データをコピーせずに利用できる
他の言語の ポインタ と似ているが、生存期間制約 と 借用ルール によって再利用が安全に行える
一方、参照を用いるとコードが扱いづらくなるというデメリットもある
これは、フィールドを 参照 で保持するデータ構造でよく現れる
code:rs
#derive(Clone, Debug)
pub struct Tlv<'a> {
pub type_code: u8,
pub value: &'a u8,
}
特にインスタンスを引き回す場合、面倒なことになる
この問題は 所有 することで回避できることが多い
code:rs
#derive(Clone, Debug)
pub struct Tlv {
pub type_code: u8,
pub value: Vec<u8>,
}
大きくて悪いコピーなんて怖くない
Rustacean はコピーを避けがち
∵ コピーとメモリ確保が発生していることが明確になるため
e.g.
.to_vec や .clone などのメソッド呼び出し
Box::new 関数の呼び出し
しかし、明示的になっているからといって、最適化 して取り除く理由にはならない
特に、最適化で利便性が犠牲になる場合は、まずは使いやすさに焦点を当てて「パフォーマンスが本当に重要な場合にだけ、最適化するようにしよう」
ベンチマーク 測定の結果、コピーを減らすことで改善できる場合だけにすべき
とはいえ、2 つ覚えておくことがある
1. 「一般に」コピーは明示的に行われる
Copy トレイトを実装している場合は、ムーブセマンティクス から コピーセマンティクス に移行する(勝手にコピーする)
ただし、「Copy トレイトはビット単位のコピーが有効で高速である場合でのみ実装しよう」
項目10:標準トレイトに習熟しよう
e.g. 追加データを持たない enum 型
2. alloc を利用できる no_std な ビルドターゲット を選択すると、no_std の制約を回避しつつ、使いやすさや再利用性の高い開発が可能になる
再利用性の高い
∵ no_std の方がどんな環境下でも実行できるため
項目33:ライブラリコードをno_std互換にすることを検討しよう
参照と スマートポインタ
データ構造がフィールドを所有すると、変更があった時にコピーをすべて同期して更新する必要がある
この問題はスマートポインタを用いて、単一所有から共有所有に移行することで回避できる
項目8:参照型とポインタ型に慣れよう#677789b075d04f00001041e4
例えば Rc や Arc は 参照カウンタ を用いて共有所有を実現する
加えて ミュータブル な操作が必要となる場合、内部可変性 を実現する内部型と組み合わせると良い
RefCell: シングルスレッド で内部可変性を実現するために用いる
一般的に Rc<RefCell<T>> のように用いる
Mutex: マルチスレッド で内部可変性を実現するために用いる
一般的に Arc<Mutex<T>>
スマートポインタを 最後の手段 として使う必要はない
つまり、複雑に絡み合った参照の生存期間を扱う代わりに、スマートポインタを設計に取り入れることは 負けを認める ことではない
むしろ、「スマートポインタを使った方が簡潔で、保守しやすく、使いやすい設計になる場合もある」
#Rust #Effective_Rust_―_Rustコードを改善し、エコシステムを最大限に活用するための35項目