Rustの最も強力な機能の一つは、matchコマンドを介して実装されたパターンマッチングです。Rust 1.26のリリースとともに、"match ergonomics"という用語が登場しました。これは、マッチ式の中で参照値を自動的にデリファレンスし、明示的なrefや*をパターン内で減少させる新たなロジックを意味します。
マッチエルゴノミクスが登場する前は、リンクで値をラップする列挙型(例えば、Option<&T>)を使用する際に、パターンのデリファレンスを明示的に指定する必要がありました:
let opt: Option<&i32> = Some(&10); match opt { Some(&val) => { /* ... */ }, None => {}, }
これにより、リファレンスのレベルでの読みやすさが低下し、複雑な構造の中で間違える可能性が増しました。
マッチエルゴノミクスが導入されたことで、Rustはパターン内のリファレンスを自動的にデリファレンスし、より簡単で自然なコードを書くことができるようになりました:
let opt: Option<&i32> = Some(&10); match opt { Some(val) => println!("{}", val), None => (), }
コンパイラは、プログラマーの便宜のために&i32をi32にデリファレンスする必要があることを"理解"します。この機能により、Option、Result、及びそれらの参照との組み合わせ型の作業が大幅に簡素化されます。
主な特徴:
*やrefの減少パターン内でrefを使用することと値への参照(例えば、ref val vs &val)との違いは何ですか?
refはスタック上の値への参照を作成しますが、&valは既存のリファレンスと一致します。これは、ミュータビリティで作業するために追加の参照が必要な場合には重要です。
例:
let x = 5; match x { ref r => println!("ref: {}", r), // r: &i32 }
Option<&T>でパターンマッチングを行う際にミュータブルリファレンスを使用すると何が起こりますか?
自動デリファレンスはミュータブルリファレンスでも機能します。Option<&mut T>を持っている場合、マッチを通じてミュータブルな変数を直接取得できます。
例:
let mut x = 5; let opt = Some(&mut x); match opt { Some(val) => *val += 1, None => {} }
マッチエルゴノミクスは明白でない借用や所有権のエラーを引き起こす可能性がありますか?
はい、マッチの中で一時的な借用が作成され、それがマッチの全てのブランチに作用し、この値に対する他の操作をブロックする可能性があることを認識しなければなりません。
*やrefを使用し、コードの簡潔さと可読性を損なう初心者が古いスタイルのSome(&val)やSome(ref val)を使い続け、新しいRustバージョンに更新した際にエラーまたは予測不可能な動作を受ける
利点: コードはRustの初期バージョンで動作し、マッチエルゴノミクスの登場前に学んだ人には理解しやすい
欠点: 表現力が低く、コンパイラのアップデートに伴うエラーのリスクがある
プログラマーが最新のマッチエルゴノミクスを使用し、コードがコンパクトで自動デリファレンスを効果的に活用している
利点: 簡潔で読みやすく、参照のレベルで間違うリスクが減少する
欠点: 古いRustバージョンには適さず、現代のイディオムにあまり触れたことのない人には理解が難しいかもしれない