１９章ー３節：高度な機能ー型についての発展的な事項


## 目次
  - [19.3.0 型についての発展的事項の概要](#1930-型についての発展的事項の概要)
  - [19.3.1 型安全性と抽象化のためのニュータイプパターンの使用](#1931-型安全性と抽象化のためのニュータイプパターンの使用)
  - [19.3.2 型のエイリアスを使用して型の同義語を作成する](#1932-型のエイリアスを使用して型の同義語を作成する)
  - [19.3.3 never 型は絶対に返らない](#1933-never-型は絶対に返らない)
  - [19.3.4 動的サイズ型と `Sized` トレイト](#1934-動的サイズ型と-sized-トレイト)
    - [`str` 型について](#str-型について)
    - [トレイトについて](#トレイトについて)
    - [Sized トレイトについて](#sized-トレイトについて)

## 19.3.0 型についての発展的事項の概要

- ニュータイプパターンが有用な理由
  - 型をつけることで異なる種類の値同士が混同されることを防ぐことができる
    - 例えば、`Meters(u32)` と `Millmeters(u32)` を混同せずに済む
  - 型の実装の詳細を抽象化することもできる
    - 例えば、ID と人名を関連付けて格納する `HashMap<i32, String>` を `People` 型でラップすることで以下のような恩恵が得られる
      - `People` コレクションにメソッドを適切に定義することで、`HashMap` の提供するすべてのAPI ではなく、自分たちの定義したパブリックな API とだけやり取りするようにカプセル化を施すことができる

- 型エイリアス
  - 型に "あだ名" をつけることができる
  - たとえば `type Kilometers = i32;` とすると、`i32` 型を指す際に `Kilometers` とすることができる
  - これは、複雑な型をコード内で何度も使用する必要があるときに有用
    - たとえば `type Thunk = Box<dyn Fn() + Send + 'static>;` のようにすると便利

- never 型 `!`
  - `todo!()`、`panic!()` や ループ中の `break;` と `continue;`、他には `break;` を含まない `loop{...}` などは never 型 `!` を返す
  - この `!` はどのような型にも型強制される
    - &rarr; `match` 式の一部のアームで使用した際に、一見すると型が食い違う場合も問題なく型強制される

- 動的サイズ決定型はコンパイル時にメモリサイズが既知でない値を扱うための型
  - 例は `str` 型とすべてのトレイト
  - これらの型は、適当なポインタと組み合わせて使用する必要がある
    - たとえば、`&str` や `Box<dyn Trait>` のように

- Rust ではコンパイル時にメモリサイズが既知の値をあらわすトレイト `Sized` が提供されている
  - このトレイトはデフォルトですべての要素にあてがわれる
  - このデフォルトの処理を上書きしてジェネリック関数で動的サイズ決定型を使いたい場合は、`fn generic<T: ?Sized>(t: &T) {` のよう `?Sized` トレイト境界を指定すればよい

## 19.3.1 型安全性と抽象化のためのニュータイプパターンの使用

&rarr; 概要で述べた以上の情報なし

## 19.3.2 型のエイリアスを使用して型の同義語を作成する

- Rustには、型エイリアスを宣言して既存の型に別の名前を付ける機能がある（＝同義語を作成する）

- これには `type` キーワードを使う

- ただし、型エイリアスを使用しても、ニュータイプパターンのように型の混同を防ぐことにはならないことに注意
  - あくまで、既存の型の同義語を作る機能であって、新しい個別の型を作る機能ではない
  - 新しい型を作りたいならニュータイプパターンなどを利用すること

- 例：

  ```rust
  type Kilometers = i32;

  let x: i32 = 5;
  let y: Kilometers = 5;

  println!("x + y = {}", x + y);  // i32 と Kilometers は同じ型なので問題なくコンパイルされ、実行もされる
  ```

- この機能を使えば複雑で長ったらしい型名を何度も読み書きする必要がなくなる
  - 例：

    ```rust
    type Thunk = Box<dyn Fn() + Send + 'static>;

    let f: Thunk = Box::new(|| println!("hi"));

    fn takes_long_type(f: Thunk) {
        // --snip--
    }

    fn returns_long_type() -> Thunk {
        // --snip--
    }
    ```

- この機能の最たる例は 標準ライブラリの `std::io` モジュールの `Result<T, std::io::Error>` である

  - 標準ライブラリでは `type Result<T> = std::result::Result<T, std::io::Error>;` という型エイリアスが設定されている

  - そのため、`Result<T>` と書くだけで `Result<T, std::io::Error>` を表せる

  - 例：以下のコードは、さらにその下のコードのように書き換えられる

    ```rust
    use std::fmt;
    use std::io::Error;

    pub trait Write {
        fn write(&mut self, buf: &[u8]) -> Result<usize, Error>;
        fn flush(&mut self) -> Result<(), Error>;

        fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>;
        fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Error>;
    }
    ```

    ```rust
    use std::fmt;
    use std::io::Error;

    pub trait Write {
        fn write(&mut self, buf: &[u8]) -> Result<usize>;
        fn flush(&mut self) -> Result<()>;

        fn write_all(&mut self, buf: &[u8]) -> Result<()>;
        fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()>;
    }
    ```

## 19.3.3 never 型は絶対に返らない

&rarr; 概要で述べた以上の情報なし

## 19.3.4 動的サイズ型と `Sized` トレイト

- 動的サイズ決定型（DST, unsized 型）：
  - 実行時にしかサイズを知ることができない値を使ったコードを書けるようにしてくれている
  - これらの型は直接関数の引数に渡すことができない
    - `&`, `Box<...>`, `Rc<...>` などを使ってポインタの背後に置いて使用する必要がある
  - 最たる例は `str` 型やトレイト

### `str` 型について

- `str` 型は動的サイズ決定型
- たとえば、以下のようなコードは許されない：
  - あきらかに、`s1` と `s2` の長さ（必要なメモリ数）が異なる
  - Rust では、ある型の値はすべて同じ量のメモリを使わなければならないので、これは許されない

  ```rust
  let s1: str = "Hello there!";
  let s2: str = "How's it going?";
  ```

- これは `str` を `&str` にすることによって解消される

  - `&str` は通常の参照と異なり、ある値のメモリアドレスを格納するのみならず、文字列の長さも格納する
    - これが動的サイズ決定型 `str` が通常の型と異なる点

  - ある文字列の先頭へのポインタとその文字列の長さのみを格納するだけなので `&str` 型はコンパイル時にメモリサイズがわかる

- 実は `str` はあらゆるポインタと組み合わせることができる
  - たとえば、`Box<str>` や `Rc<str>` などにすることもできる

### トレイトについて

- すべてのトレイトも動的サイズ決定型である
  - トレイトも `str` と同様に、ポインタと組み合わせることで、型を指定するのに利用できる
    - たとえば、`&dyn Trait`, `Box<dyn Trait>`, `Rc<dyn Trait>` のように
  - 実は、トレイトオブジェクトがポインタと組み合わせて生成する必要があったのも、トレイトが動的サイズ決定型だからである

### Sized トレイトについて

- Rust には、型のサイズがコンパイル時に既知かどうかを判断するための `Sized` トレイトが存在する

- このトレイトはコンパイル時にサイズが既知のすべてに対して自動的に実装される

- さらに、Rust はすべてのジェネリック関数に `Sized` の境界を暗黙的に追加する
  - つまり、以下のようなコードは、さらにその下のコードに置き換えられてコンパイルされると考えればよい

    ```rust
    fn generic<T>(t: T) {
        // --snip--
    }
    ```

    ```rust
    fn generic<T: Sized>(t: T) {
        // --snip--
    }
    ```

- デフォルトでは、ジェネリック関数はコンパイル時に既知のサイズを持つ型に対してのみ機能する

- しかし、以下の特別な構文を使用すればこの制限を緩和できる：

  ```rust
  fn generic<T: ?Sized>(t: &T) {
      // --snip--
  }
  ```

- `?Sized` というトレイト境界は、「TはSizedであってもなくてもよい」という意味を持つ
- また、`t` の型を `T` から `&T` に変更したことにも注意してほしい
  - 型が `Sized` でない可能性があるため、何らかのポインターの後ろで使う必要があるためこのようにしている


## 参考文献

- [The Rust Programming Language 日本語版](https://doc.rust-jp.rs/book-ja/ch19-04-advanced-types.html)