Rustでは、type構文を使用して既存の型のエイリアスを作成できます。例えば:
type Kilometers = i32; fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { x + y }
Kilometersは単なるi32の別名であり、コンパイラはこれらの型を区別せず、完全に互換性があります。
Newtype patternは、既存の型のための新しいラッパー構造体を作成することです:
struct Kilometers(i32); // 新しい別個の型 fn add_distance(x: Kilometers, y: Kilometers) -> Kilometers { Kilometers(x.0 + y.0) }
これでKilometersとi32は異なる型になり、混同されることはありません。
これらのアプローチの選択は、安全性と可読性の要件によって異なります。オープンAPIや特に型安全な単位のためには、newtypeを使用してください。
Rustにおける
typeエイリアスとnewtype patternの違いは何ですか?エイリアスタイプに対してトレイトの実装を通じて操作を制限できますか?
多くの場合、エイリアスを使用してアクセス制御や操作の制限を行うことができると答えますが、これは間違いです—エイリアスは新しい型を生成しないため、ユニークなトレイトを実装することはできません。新しい型を持つnewtype patternだけが、特別な動作を実装できる型です。
type UserId = u64; struct UserIdNew(u64); trait Foo { fn foo(&self); } // UserIdに対してFooを実装することはできません。なぜなら、それは単なるu64だからです。 // UserIdNewには実装可能です。
事例
プロジェクトでは、エイリアスを使用してメートルとミリメートルで距離を測定していました:
type Millimeters = u32; type Meters = u32;結果的に、偶然ミリメートルとメートルを足し算してしまい、計算エラーを引き起こしました。 型チェックは機能せず、コンパイラはこれを検出しませんでした。newtypeに切り替えたところ、問題が解決しました。
事例
オープンAPIでは、エイリアスを使用して異なるエンティティの識別子(
type UserId = u64;、type OrderId = u64;)を利用していました。混乱が生じ、誰かがパラメータを間違えて入れ替え、バグがプロダクションに入ってしまいました。型によってそれを区別できなかったためです。
事例
開発者は、型エイリアスに対してDisplayトレイトのユニークな動作を実装しようとしていました:
type MyType = String; impl Display for MyType { ... }コンパイラは「型エイリアスに対してトレイトを実装できません」というエラーを出しました。問題はnewtype patternを通じて解決されました。