TL;DR
1 つのフィールドを持つタプル構造体を使って、既存の型を包む不透明なラッパを作る Rust のデザインパターン。Rust Design Patterns で紹介されている。
なぜ必要か
Rust Design Patterns によると、Newtype の主な目的は抽象化である。実装の詳細を共有しながら、公開するインタフェースを細かく制御でき、互換性を保ったまま実装を変えられる。f64 を包んで Miles と Kilometres を別の型にするように、単位を区別するのにも使える。包んだ型と元の型は取り違えられない。
仕組み
型エイリアスと違い、元の型とは別の新しい型が作られる。それでいて、実行時の追加のオーバーヘッドは発生しない(ゼロコスト抽象化)。クラスで包むと実行時のオーバーヘッドが発生する。Scala に Opaque types が生まれたのも、これを避けるためである。
例
#[derive(Debug, Clone, Copy)]
pub struct BookId(uuid::Uuid);
impl BookId {
pub fn new() -> Self {
Self(uuid::Uuid::new_v4())
}
}注意点
Rust には Newtype を特別に支える言語機能がないので、定型のコードが増えやすい。元の型のメソッドを公開したければメソッドごとに中継するメソッドを書き、実装したいトレイトごとに impl を書く必要がある。
自分の理解
オブジェクト指向プログラミングにおける Value Object のようなものと解釈している。