0Pricing
Learn Rust Coding · レッスン

Newtypeパターン

安全性と明確さのために型をラップします。

「Newtypeパターン」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。

このレッスンの一部はまだ翻訳されておらず、英語で表示されています。

What Is a Newtype?

A newtype is a single-field tuple struct that wraps an existing type to give it a distinct identity. struct Meters(f64) is a brand new type even though it holds a plain f64.

The wrapper has zero runtime cost but lets the compiler enforce meaning that a raw primitive cannot.

struct Meters(f64);
struct Seconds(f64);

Preventing Unit Mix-Ups

Raw primitives are easy to confuse. If both a distance and a time are f64, nothing stops you swapping them in a call.

Wrapping each in its own newtype makes such mistakes a compile error instead of a silent bug.

fn speed(d: Meters, t: Seconds) -> f64 {
    d.0 / t.0
}
// speed(Seconds(2.0), Meters(10.0)) -> compile error

Accessing the Inner Value

You reach the wrapped value through tuple index .0. Many newtypes also expose a method or implement From for ergonomic conversions.

struct UserId(u64);
impl UserId {
    fn value(&self) -> u64 { self.0 }
}
fn main() {
    let id = UserId(42);
    println!("id = {}", id.value());
}

Encapsulating Invariants

Make the inner field private and validate in a constructor. Then any value of the newtype is guaranteed valid, so downstream code never re-checks.

pub struct Email(String);
impl Email {
    pub fn new(s: &str) -> Option<Email> {
        if s.contains('@') {
            Some(Email(s.to_string()))
        } else {
            None
        }
    }
}

The Orphan Rule

Rust forbids implementing a foreign trait for a foreign type. You cannot write impl Display for Vec<T> because you own neither the trait nor the type.

This rule keeps trait coherence sound across crates, but it can block useful impls.

Newtypes Bypass the Orphan Rule

Because the newtype is defined in your crate, you now own a local type and may implement any trait for it. This is the standard workaround for the orphan rule.

use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

A Runnable Wrapper Display

Here the wrapper from the previous scene is put to work. We own Wrapper, so implementing Display for it is allowed and the program prints the joined list.

use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}
fn main() {
    let w = Wrapper(vec!["a".into(), "b".into()]);
    println!("{}", w);
}

Restricting the API Surface

Wrapping a powerful type lets you expose only a safe subset. A NonEmptyVec can hide mutating methods that would let it become empty, preserving its invariant.

pub struct NonEmptyVec<T>(Vec<T>);
impl<T> NonEmptyVec<T> {
    pub fn new(first: T) -> Self {
        NonEmptyVec(vec![first])
    }
    pub fn first(&self) -> &T {
        &self.0[0]
    }
}

Zero-Cost Abstraction

A newtype with one field has the same memory layout as the wrapped value. The compiler optimizes the wrapper away, so safety here is genuinely free at runtime.

Adding #[repr(transparent)] guarantees identical layout, which matters for FFI.

#[repr(transparent)]
struct Celsius(f64);

Deriving Traits on Newtypes

Newtypes often derive standard traits so they behave like the inner value where appropriate. Deriving keeps them ergonomic for keys, comparisons, and debug output.

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct ProductId(u32);
fn main() {
    let a = ProductId(7);
    let b = a.clone();
    println!("{:?} == {:?}: {}", a, b, a == b);
}

Newtype Versus Type Alias

Do not confuse a newtype with a type alias. type Meters = f64 is just a name; it is still an f64 and offers no extra safety.

A newtype struct Meters(f64) is a genuinely distinct type the compiler can keep separate.

type Km = f64;       // alias: interchangeable with f64
struct Mi(f64);      // newtype: distinct from f64

Quick Check

Decide why a newtype helps where a type alias does not.

Recap

The newtype pattern wraps an existing type in a one-field tuple struct to gain a distinct identity at zero runtime cost. It prevents value mix-ups, encapsulates invariants behind a private field, and sidesteps the orphan rule so you can implement foreign traits.

Unlike a type alias, a newtype is a real, separate type.

よくある質問

「Newtypeパターン」レッスンは無料ですか?

はい。「Newtypeパターン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。

「Newtypeパターン」で何を学びますか?

安全性と明確さのために型をラップします。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Learn Rust Codingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「Newtypeパターン」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLearn Rust Codingレッスンでコードを書いて実行できますか?

はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Builderパターン
  2. Newtypeパターン
  3. 型状態Builder
  4. Derefとラッパーの使いやすさ
← Learn Rust Codingに戻る