スライスの実装を通してRustのRc, RefCellの理解度が分かるコード例 (Go, Julia との比較)

## 概要
最近、単方向リストを試しに実装してみたが、Rc や RefCell の理解度が大いに試された。それもあって、腕試しになりそうな題材を乗せる。

### 環境

* rustc 1.57.0 (Rust 2021)
* WSL1 (Ubuntu 18.04 LTS)

## 問題の紹介

次のプログラムが Rust で書けると Rc, RefCell の基本的な理解ができていると思います。自信のあるひとは解答をみないで書いてみてください。

### 実力チェック (Go, Julia の例)

以下の配列で、競合する要素 b[0] を `XXX` に書き換えてみるというものです。

|index  | names  | a=names[0:2] | b=names[1:3] | 期待値 |
|------:|--------|--------------|--------------|--------|
|     0 | John   | John         |              | John   |
|     1 | Paul   | Paul         | Paul <= XXX に書き換え         | XXX    |
|     2 | George |              | George       | George |
|     3 | Ringo  |              |              | Ringo  |

Go と Julia だとこんな感じです。ざっと調べた感じだと、Pythonあたりは標準ではできなさそうですね。

#### Goでの実装例

```go
package main

import "fmt"

func main() {
  names := [4]string{
    "John", "Paul", "George", "Ringo",
  }
  fmt.Println(names)

  a := names[0:2]
  b := names[1:3]
  fmt.Println(a, b)

  b[0] = "XXX"
  fmt.Println(a, b)
  fmt.Println(names)
}
```

**実行例**

```
[John Paul George Ringo]
[John Paul] [Paul George]
[John XXX] [XXX George]
[John XXX George Ringo]
```

#### Juliaでの実装例

`@view` を使います。

```Julia
names = ["John", "Paul", "George", "Ringo"]
println("names=$(names)")

a = @view names[1:2]
b = @view names[2:3]
println("names[1:2]=$(a) names[2:3]=$(b)")

println("names[2:3][1]=XXX")
b[1] = "XXX"
println("names[1:2]=$(a) names[2:3]=$(b)")
println("names=$(names)")
```

**実行例**

```
names=["John", "Paul", "George", "Ringo"]
names[1:2]=["John", "Paul"] names[2:3]=["Paul", "George"]
names[2:3][1]=XXX
names[1:2]=["John", "XXX"] names[2:3]=["XXX", "George"]
names=["John", "XXX", "George", "Ringo"]
```

#### Pythonでの実装(NG)

listではできない模様

```Python
names = ["John", "Paul", "George", "Ringo"]
print(f"names={names}")

a = names[0:2]
b = names[1:3]
print(f"names[1:2]={a} names[2:3]={b}")

print("names[2:3][1]=XXX")
b[1] = "XXX"
print(f"names[1:2]={a} names[2:3]={b}")
print(f"names={names}")
```

```
names=['John', 'Paul', 'George', 'Ringo']
names[1:2]=['John', 'Paul'] names[2:3]=['Paul', 'George']
names[2:3][1]=XXX
names[1:2]=['John', 'Paul'] names[2:3]=['Paul', 'XXX']
                    ^^^^^^ NG
names=['John', 'Paul', 'George', 'Ringo']
               ^^^^^^ NG
```

### Rustでの実装例 (コンパイルエラー)

まずは、RustのスライスでGoやJuliaと同じように実装すると怒られます。

```rust
fn main() {
    let mut names = [
        String::from("John"),
        String::from("Paul"),
        String::from("George"),
        String::from("Ringo")
    ];
    println!("{:?}", names);

    let a = &names[0..2];
    let b = &mut names[1..3];  // ここ！
    println!("{:?} {:?}", a, b);

    b[0] = String::from("XXX");
    println!("{:?} {:?}", a, b);
    println!("{:?}", names);
}
```

Immutable(変更不可)なスライス同士ならいいのですが、すでに Immutable のスライスがあると、規則違反で怒られます。

```
error[E0502]: cannot borrow `names` as mutable because it is also borrowed as immutable
  --> slice_pointers.rs:11:18
   |
10 |     let a = &names[0..2];
   |              ----- immutable borrow occurs here
11 |     let b = &mut names[1..3];
   |                  ^^^^^ mutable borrow occurs here
12 |     println!("{:?} {:?}", a, b);
   |                           - immutable borrow later used here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
```

### Rustでの実装例

他の言語と異なり、Rustでは次の操作で実装指針が異なる。

* 参照先の値を変更できるようにする
* 値を置換する

#### Rc\<RefCell\<String>>版

もともとの Go のサンプルだと値も書き換えられるので、まずは `Rc` と `RefCell` を使います。上位互換的存在ですね。

複数の所有者 `vec![..]` とスライス `b` が居るので Rc を通じて共有ができます。Rcを使わないケースでは、コピーなりの動作で別物です。(ポインタを出力すると違いが分かります)

RefCell では一時的に更新ができるようになります。これにより、コンパイルでは規則違反にならずに済みます。`*b[0].borrow_mut() = String::from("XXX")` の部分は、今回の例だと `b[0].replace(String::from("XXX"));` でもよい。

```rust
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let names = vec![
        Rc::new(RefCell::new(String::from("John"))),
        Rc::new(RefCell::new(String::from("Paul"))),
        Rc::new(RefCell::new(String::from("George"))),
        Rc::new(RefCell::new(String::from("Ringo")))
    ];
    println!("names={:?}", names);

    let a = &names[0..2];
    let b = &names[1..3]; // mut 指定は不要で
    println!("a={:?} b={:?}", a, b);

    *b[0].borrow_mut() = String::from("XXX");
    println!("a={:?} b={:?}", a, b);
    println!(
        "names={:?}",
        names.iter().map(
            |x| x.borrow().clone()
        ).collect::<Vec<String>>()
    );
}
```

#### RefCell\<String>版
実はスライスを使っている関係で Rc は必要ないという…ことに気づく。
`b[0].borrow_mut().push_str("a");` のようなこともできる。

```rust
use std::cell::RefCell;

fn main() {
    let names = [
        RefCell::new(String::from("John")),
        RefCell::new(String::from("Paul")),
        RefCell::new(String::from("George")),
        RefCell::new(String::from("Ringo")),
    ];
    println!("names={:?}", names);

    let a = &names[0..2];
    let b = &names[1..3];
    println!("a={:?} b={:?}", a, b);

    b[0].replace(String::from("XXX"));
    println!("a={:?} b={:?}", a, b);
    println!(
        "names={:?}",
        names.iter().map(
            |s| s.borrow().clone()
        ).collect::<Vec<_>>()
    );
}
```

#### RefCell\<&str>版

`&str` でも実装できる。不変オブジェクトなので `borrow()` は使えても `borrow_mut()` は使えないことに注意する。

```rust
use std::cell::RefCell;

fn main() {
    let names = [
        RefCell::new("John"),
        RefCell::new("Paul"),
        RefCell::new("George"),
        RefCell::new("George"),
    ];
    println!("names={:?}", names);

    let a = &names[0..2];
    let b = &names[1..3];
    println!("a={:?} b={:?}", a, b);

    b[0].replace("XXX");
    println!("a={:?} b={:?}", a, b);
    println!(
        "names={:?}",
        names.iter().map(
            |s| s.borrow().clone()
        ).collect::<Vec<_>>()
    );
}
```

#### Cell\<&str>版

[std::cell::Cell](https://doc.rust-lang.org/std/cell/struct.Cell.html)と[str](https://doc.rust-lang.org/stable/std/primitive.str.html)を使った例です。

Cellは内部可変性を持ちスライスの宣言が Immutable でも値の置き換えができます。なお、 `Cell` の `get()` は Copyトレイトを要求するので、`Cell<String>` 版は実装できない。

`set(...)` の代わりに `replace(...)` も使えるが復帰値を使わない場合は `set(...)` がよりシンプルである。

```rust
use std::cell::Cell;

fn main() {
    let names = [
        Cell::new("John"),
        Cell::new("Paul"),
        Cell::new("George"),
        Cell::new("Ringo")
    ];
    println!("names={:?}", names);

    let a = &names[0..2];
    let b = &names[1..3];
    println!("a={:?} b={:?}", a, b);

    b[0].set("XXX");
    println!("a={:?} b={:?}", a, b);
    println!(
        "names={:?}",
        names.iter().map(
            |s| s.get()
        ).collect::<Vec<_>>()
    );
}
```

**実行結果**

```
names=[Cell { value: "John" }, Cell { value: "Paul" }, Cell { value: "George" }, Cell { value: "Ringo" }]
a=[Cell { value: "John" }, Cell { value: "Paul" }] b=[Cell { value: "Paul" }, Cell { value: "George" }]
a=[Cell { value: "John" }, Cell { value: "XXX" }] b=[Cell { value: "XXX" }, Cell { value: "George" }]
names=["John", "XXX", "George", "Ringo"]
```

## まとめ
スライス周りは、言語による思想の違いが出て興味深いですね。

Rustは競合の要因をプログラマー以上にコンパイラーが知っていて、それに気づかせてくれます。大抵、コンパイラーを解消していると競合の問題の大半は消滅しています。

この例は単純ですが、リストを自作して見ると同じようなエラーではまります(それは別途)。