【にゅーたいぷぱたーん】

Newtypeパターン とは?

最終更新:
💡 同じ「数字」でも「ユーザーID」と「金額」を別物として扱う型の仕切り役

既存の型を薄いラッパーで包み、別の型として扱うパターン。同じ数値型でもユーザーIDと注文IDを区別し、型が合わない受け渡しを検出できる。単なる型の別名とは異なる。

📌 このページのポイント
中身が同じでも、型で意味を分けるRustのタプル構造体の例UserId(42)ユーザーIDの型中身は整数42OrderId(42)注文IDの型中身は整数42型が合う型が違うUserIdを受け取る関数OrderIdはそのまま渡せない別名だけでは別の型になるとは限らない
UserIdとOrderIdを別のタプル構造体で定義した例。青い矢印は引数の受け渡し、赤い×は型の不一致を示します。値の形式や実在性の検証は別に必要です。
ひよこ ひよこ
Newtypeパターンって普通の型と何が違うの?
ペンギン先生 ペンギン先生
例えばRustでstruct UserId(u64);とstruct OrderId(u64);を定義すると、中身は同じ整数でも別の型になるよ。UserIdを受け取る関数にOrderIdを渡すと、型が合わないのでコンパイル時に検出できるんだ
ひよこ ひよこ
型に名前を付ければいいの?
ペンギン先生 ペンギン先生
別名だけでは区別できない場合があるよ。Rustのtype UserId = u64;はu64の別名で、新しい型ではない。意味を分けたいときは、タプル構造体などで本当に別の型を作るんだ
ひよこ ひよこ
中身は同じ数値だけど、実行時の費用は?
ペンギン先生 ペンギン先生
Rustの公式書籍は薄いラッパー自体に実行時の性能上の追加負担はないと説明しているよ。Haskellのnewtypeも元の型と同じ実行時表現を使う。ただし値を検証したり別のデータを確保したりする処理の費用までなくなるわけではないんだ
ひよこ ひよこ
TypeScriptでもできるの?
ペンギン先生 ペンギン先生
例えばtype UserId = string & { _brand: "UserId" };のようなブランド付きの型で、似た区別を表せるよ。TypeScriptは基本的に値の構造で型を比べるので、ブランドで違いを加える方法なんだ。ただしasなどで型を指定すると、この区別を迂回できることもあるよ
ひよこ ひよこ
型を分ければ、存在しないユーザーIDも防げる?
ペンギン先生 ペンギン先生
それは別の検証が必要だよ。型を分けるのは、ユーザーIDと注文IDなどの取り違えを減らすため。文字列の形式やユーザーの存在まで自動で確認するものではない。外部入力を確かめてから、その型の値を作る処理も考えるんだ
ペンギン
まとめ:ざっくりこれだけ覚えればOK!
「Newtypeパターン」って出てきたら「同じ種類の値を、意味ごとに別の型で包む方法」と思えばだいたいOK!
📖 おまけ:英語の意味
「Newtype Pattern」 = 既存の型を包んで別の型にするパターン
💬 Rust公式書籍は、Newtypeという名前がHaskellに由来すると説明しているよ。Haskellにはnewtypeという宣言があり、元の型と実行時の表現を共有する別の型を作る

参考資料

← 用語集にもどる