Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Newtype

場合によっては、ある型を別の型と同じように振る舞わせたい、または 型エイリアスだけでは不十分なときに、コンパイル時に何らかの振る舞いを 強制したいことがあります。

たとえば、セキュリティ上の考慮事項(例: パスワード)により、String に対してカスタムの Display 実装を作成したい場合です。

このような場合、Newtype パターンを使用して 型安全性カプセル化 を提供できます。

説明

単一のフィールドを持つタプル構造体を使用して、ある型の不透明なラッパーを作成します。 これにより、型のエイリアス(type アイテム)ではなく、新しい型が作成されます。

use std::fmt::Display;

// String の Display トレイトをオーバーライドするために Newtype Password を作成する
struct Password(String);

impl Display for Password {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "****************")
    }
}

fn main() {
    let unsecured_password: String = "ThisIsMyPassword".to_string();
    let secured_password: Password = Password(unsecured_password.clone());
    println!("unsecured_password: {unsecured_password}");
    println!("secured_password: {secured_password}");
}
unsecured_password: ThisIsMyPassword
secured_password: ****************

動機

newtype の主な動機は抽象化です。型どうしで実装の詳細を共有しつつ、インターフェイスを正確に制御できます。 API の一部として実装型を公開するのではなく newtype を使用することで、後方互換性を保ちながら実装を変更できます。

newtype は単位を区別するために使用できます。たとえば、f64 をラップして、区別可能な MilesKilometres を与えることができます。

利点

ラップされた型とラッパー型には型の互換性がありません(type を使用する場合とは異なります)。そのため、newtype のユーザーがラップされた型とラッパー型を「混同」することは決してありません。

newtype はゼロコスト抽象化であり、実行時のオーバーヘッドはありません。

プライバシーシステムにより、ユーザーはラップされた型にアクセスできません(フィールドが private である場合。これはデフォルトです)。

欠点

newtype の欠点(特に型エイリアスと比較した場合)は、特別な言語サポートがないことです。これは、大量の ボイラープレートが発生する可能性があることを意味します。 ラップされた型で公開したい各メソッドに対して「パススルー」メソッドが必要であり、ラッパー型にも実装したい各トレイトに対して impl が必要です。

議論

newtype は Rust コードで非常によく使われます。抽象化や単位の表現が最も一般的な用途ですが、他の理由でも使用できます。

  • 機能を制限する(公開される関数や実装されるトレイトを減らす)、
  • コピーセマンティクスを持つ型にムーブセマンティクスを持たせる、
  • より具体的な型を提供し、それによって内部型を隠すことによる抽象化。 例:
pub struct Foo(Bar<T1, T2>);

ここで、Bar は何らかの公開されたジェネリック型であり、T1T2 は何らかの内部型である可能性があります。 私たちのモジュールのユーザーは、FooBar を使用して実装していることを知るべきではありませんが、ここで実際に隠しているのは T1T2 という型と、それらが Bar とともにどのように使用されるかです。

関連項目