【Rust勉強】スマートポインタをざっくり

# はじめに
https://doc.rust-jp.rs/book-ja/ch15-00-smart-pointers.html

この記事は公式ドキュメントを読んでRustを勉強している時のメモを軽く清書したものです。

簡単に理解できたところはあまり書いてない傾向にあるので、全ては書いてありません。

みんな公式ドキュメント読もうね。あとタコピーの原罪も読もうね。

# スマートポインタ

- ポインタのように振る舞うかつ追加のメタデータと能力があるデータ構造。

- 通常構造体を利用して定義されていて、普通の構造体との違いは`Deref`と`Drop`トレイトを実装していること。

- ここで紹介する`Box`、`Rc`、`Refcell`は代表的なだけで他にもスマートポインタはある。

## Box

- `Box<T>`はスタックに格納されるデータをヒープに格納することができる。その時スタックに残るのはヒープへのポインタ。

### Boxでヒープにデータを格納する

例えばこのプログラムは`i32`の値がヒープに保存される。

```rust
fn main() {
    let b = Box::new(5);
    println!("b = {}", b);  // b = 5
}
```

通常の値と同じように、スコープを抜けた時「ヒープに格納されている値」と「スタックに格納されているポインタ」が開放される。

単独の値をヒープにおいてもあまり嬉しくないからこういう使われ方はあまりしない。

### Boxで再帰的な型を定義する

再帰的な型は理論上無限に続く可能性があるので、コンパイル時にどれくらいのメモリ領域が必要なのか計算できない。そこで定義に`Box`を挟むことで、`Box`のさいずはわかっているので解決できる。

### コンスリストをBoxで定義する

これは再帰的なリスト。なぜなら自身の要素の`Cons`は引数に自分自身を含むから。

```rust
enum List {
    Cons(i32, List),
    Nil,
}
```

しかし、このままコンパイルすると無限のサイズが必要だと怒られる。

まず、コンパイラはこの`enum`を見た時に、`Cons`の中身がどれくらいに領域が必要なのかを確認しようとする。`Cons` の中には`List`が含まれているので`List`の中をもう一度確認しに行く。この中には`Cons`が入っているので…と無限に続く。
そのため定義ができないのだ。

そこで`Cons`に渡す`List`を`Box`でヒープに格納し、そのポインタを渡す。こうすることでコンパイラはポインタの領域だけを確保すれば良いのでコンパイルが通る。

```rust
enum List {
    Cons(i32, Box<List>),
    Nil,
}

use List::{Cons, Nil};

fn main() {
    let list = Cons(1,
        Box::new(Cons(2,
            Box::new(Cons(3,
                Box::new(Nil))))));
}
```

`Box`がスマートポインタなのは`Deref`トレイトと`Drop`トレイトがあるからだ。これらのトレイトのおかげで

- `Box<T>`を参照のように扱うことができる

- `Box<T>`値がスコープを抜けると、ボックスが参照しているヒープデータも片付けられる

の機能が実装されている。

## Derefトレイト

Boxはヒープに保存されることを無視すると、１変数のタプルのような形をしている。

```rust
struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}
```

**しかしこれだけでは、`MyBox`は本家`Box`のように`*`で参照外しはできない。それを解決するのが`Deref`トレイトである。**

`Deref`トレイトは、標準ライブラリで提供されていて`deref`メソッドの実装を要求する。

`deref`メソッドは、`self`を借用して内部のデータへの参照を返すメソッド。

```rust
use std::ops::Deref;

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}
```

`MyBox`に`Deref`トレイトを実装することで、`*`演算子が機能する。`*x`は実はRustは実際には`*(x.deref())`というコードを実行している。

### 参照外し型矯正

参照外し型矯正とは、`Deref`を実装している型への参照を`Deref`が元の型を変換できる型への参照に変換する機能。

？？？？？？？？？？

具体的に見ましょう。こんな`hello`関数を考える。

```rust
fn hello(name: &str) {
    println!("Hello, {}!", name);
}
```

`hello`は文字列スライスを引数として呼び出す。例えば`hello("Takopi")`みたいに。

`hello`に`MyBox<String>`値とともに呼び出すには通常こうする必要がある。

```rust
fn main() {
    let m = MyBox::new(String::from("Takopi"));
    hello(&(*m)[..]);
}
```

`hello`の引数で`m`の型の推移はこう。

| 引数          | 型               |
|:-----------:|:---------------:|
| `m`         | `MyBox<String>` |
| `*m`        | `String`        |
| `&(*m)[..]` | `&str`          |

最初`m`は`MyBox<String>`で覆われているので`*`で参照外しできる。

`*m`は`String`なので`&` と`[..]`で文字列全体のスライスをとって代入している。

うーんこれは面倒。しかし、参照外し型矯正のおかげでこう書ける。

```rust
fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&m);
}
```

 コンパイラは`Deref`トレイトが実装されている型を自動的に参照外しする。

流れはこんな感じ。

| 引数   | 型                | 説明                               |
|:----:|:----------------:|:--------------------------------:|
| `m`  | `MyBox<String>`  |                                  |
| `&m` | `&MyBox<String>` |                                  |
| `&m` | `&String`        | `MyBox<T>`は`Deref`を持ち、参照外し型矯正される |
| `&m` | `&str`           | `String`は`Deref`を持ち、参照外し型矯正される   |

 `&m`は`&MyBox<T>`を表しているが、`MyBox<T>`は`Deref`があるので、`&MyBox<T>`→`&String`になる。また`String`は標準で`Deref`が実装されているので、`&String`は`&str`に変換される。

## Dropトレイト

`Drop`は値がスコープを抜けそうになった時に起こることをカスタマイズできる。`Drop`はどんな型にも実装することができる。

`Drop`トレイト使う例を見よう。

```rust
struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        // CustomSmartPointerをデータ`{}`とともにドロップするよ
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}

fn main() {
    let c = CustomSmartPointer { data: String::from("my stuff") };      // 俺のもの
    let d = CustomSmartPointer { data: String::from("other stuff") };   // 別のもの
    println!("CustomSmartPointers created.");                           // CustomSmartPointerが生成された
}
```

`CustomSmartPointer` は`Drop`トレイトを持っていて、`Drop`トレイとは`drop`メソッドを持たなくてはならない。`drop`メソッドではドロップしたことを標準出力するようにしてある。

```
CustomSmartPointers created.
Dropping CustomSmartPointer with data `other stuff`!
Dropping CustomSmartPointer with data `my stuff`!
```

このように出力される。`d`が`c`より先にドロップしているのは変数は生成されたのと逆の順序でドロップされるからである。

この例では標準出力でドロップされたことを明示するだけだったが、通常は自分の型が使用した領域をクリーンアップするために使われる。

もしも、変数がドロップするよりも早く手動でドロップしたい場合に`drop`メソッドを直接呼び出すことはできない。`std::mem::drop`関数を利用する。

```rust
fn main() {
    let c = CustomSmartPointer { data: String::from("some data") };
    println!("CustomSmartPointer created.");
    drop(c);
    // CustomSmartPointerはmainが終わる前にドロップされた
    println!("CustomSmartPointer dropped before the end of main.");
}
```

```
CustomSmartPointer created.
Dropping CustomSmartPointer with data `some data`!
CustomSmartPointer dropped before the end of main.
```

mainが終了するよりも早く`c`がドロップしていることがわかる。

## Rc\<T>

単独の値が複数の所有者を持つ場合がある。複数の所有権を可能にするために使われるのが`Rc<T>`という型。

ヒープにプログラムの複数箇所で読む何らかのデータを確保したいけど、コンパイル時にどの部分が最後にデータを使用し終わるかを決定できない時に`Rc<T>`を使う。

`Rc<T>`はシングルスレッドでしか使用できないことに注意。

次の例は、`Rc<T>`を使わずに`a`の所有権を共有しようとする`Box<T>`の２つのリストを作るコード。

```rust
enum List {
    Cons(i32, Box<List>),
    Nil,
}

use List::{Cons, Nil};

fn main() {
    let a = Cons(5,
        Box::new(Cons(10,
            Box::new(Nil))));
    let b = Cons(3, Box::new(a));
    let c = Cons(4, Box::new(a));
}
```

`a`の所有権は`b`に移動しているから、`c`を作る時に`a`を使用しようとするとエラる。

`Cons`の定義を借用するように変更することもできるが、それだとライフタイム引数を指定しなければならなくなり、リストの各要素が最低でもリスト全体と同じだけ生きることを宣言する必要が出てくる。

代わりに`Box<T>`を`Rc<T>`に変更する。そして`b`、`c`を作る際は`Rc::clone`で`a`の`Rc<List>`をクローンする。この`Rc::clone`は`a.clone()`とは違い、参照カウントに1を足すだけなので時間はかからない。

```rust
enum List {
    Cons(i32, Rc<List>),
    Nil,
}

use List::{Cons, Nil};
use std::rc::Rc;

fn main() {
    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
    let b = Cons(3, Rc::clone(&a));
    let c = Cons(4, Rc::clone(&a));
}
```

`Rc<T>`の参照カウントを得るためには`Rc::strong_count`を呼び出す。

```rust
fn main() {
    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
    println!("count after creating a = {}", Rc::strong_count(&a));
    let b = Cons(3, Rc::clone(&a));
    println!("count after creating b = {}", Rc::strong_count(&a));
    {
        let c = Cons(4, Rc::clone(&a));
        println!("count after creating c = {}", Rc::strong_count(&a));
    }
    println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}
```

```
count after creating a = 1
count after creating b = 2
count after creating c = 3
count after c goes out of scope = 2
```

`Rc<T>`は単独の値に複数の所有者をもたせることができ、かつ所有者が存在している限り、値が有効であり続けることをカウントは保証してくれる。

## RefCell\<T>

- コードの特性はコードを解析するだけではわからない時がある。例えば、プログラムの停止性問題など。
- Rustコンパイラ不可能な分析にぶち当たった時は、正しいプログラムでも拒否する場合がある（保守的）。
- コードが借用規則に従っているとプログラマは確証を得ている場合は`RefCell<T>`が有効。 

## Rc\<T>とRefCell\<T>で可変データに複数の所持者をもたせる

`Rc<T>`を使ったコンスリストの例をもう一度思い出そう。

```rust
enum List {
    Cons(i32, Rc<List>),
    Nil,
}

use List::{Cons, Nil};
use std::rc::Rc;

fn main() {
    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
    let b = Cons(3, Rc::clone(&a));
    let c = Cons(4, Rc::clone(&a));
}
```
`Rc<T>`は不変値しか持たないので、一旦生成したリストの値はどれも変更できないが、
`RefCell<T>`と組み合わせること可変値をもたせることができる。

```rust
#[derive(Debug)]
enum List {
    Cons(Rc<RefCell<i32>>, Rc<List>),
    Nil,
}

use List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;

fn main() {
    let value = Rc::new(RefCell::new(5));

    let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));

    let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a));
    let c = Cons(Rc::new(RefCell::new(10)), Rc::clone(&a));

    *value.borrow_mut() += 10;

    println!("a after = {:?}", a);
    println!("b after = {:?}", b);
    println!("c after = {:?}", c);
}
```

まず`value`に`Rc<RefCell<i32>>`のインスタンス値を生成して、`value`という名前の変数に格納している。そしてコンスリスト`a`を`value`を使って生成している。

`b`、`c`の生成の時は、`a`の所有者を複数にするために`Rc::clone`をしている。

`a`、`b`、`c`を作った後、`borrow_mut`メソッドで中の値を変更する。この時自動参照外し機能が使われている。`borrow_mut`は`RefMut<T>`を返し、それに対して参照外し演算子`*`を使用して中の値を変更している。

```
a after = Cons(RefCell { value: 15 }, Nil)
b after = Cons(RefCell { value: 6 }, Cons(RefCell { value: 15 }, Nil))
c after = Cons(RefCell { value: 10 }, Cons(RefCell { value: 15 }, Nil))
```

# Box・Rc・RefCell使い分け

|                      | Box | Rc  | RefCell |
|:--------------------:|:---:|:---:|:-------:|
| 所有者                  | 単独  | 複数  | 単独      |
| 不変借用<br/>（コンパイル時の精査） | o   | o   | x       |
| 可変借用<br/>（コンパイル時の精査） | o   | x   | x       |

- `RefCell`は不変でも`RefCell`内の値を可変化できる。代わりに実行時に精査するだけなのでもし侵害した場合は`panic!`を起こす。