RUSTの型とメソッド呼び出し

### 概要
半年ほど前にRUSTをかじった後放置していましたが，最近また触り始めて不変借用，可変借用，トレイトのメソッド呼び出しで混乱したのでサンプルコードを書いて動作を確認したことをまとめておく．ただし，理解が間違っているかもしれないのであしからず．

### 前提
RUSTでは，ある型`T`とその借用型`&T`，可変借用型`&mut T`は型として区別される．以下のコードでは，構造体`S`に`self`, `&self`, `&mut self`のメソッドを定義し，呼び出したときにどのメソッドが呼び出されるのか，またはコンパイルエラーになるのかなどを確かめていく．なお，説明の都合上，参照型(&T)に対してその値の型(T)を実体型と表記する

```rust
pub fn get_type<T>(_: T) -> &'static str {
    std::any::type_name::<T>()
}

struct S { val: i32 }

impl S {
    fn new(ival: i32) -> Self {
        S { val: ival }
    }
    fn f1(self) {
        println!("val = {}", self.val);
        println!("t1 type = {}", get_type(self)); // reftest::S
    }
    fn f2(&self) {
        println!("val = {}", self.val);
        println!("t2 type = {}", get_type(self)); // &reftest::S
    }
    fn f3(&mut self) {
        self.val += 1;
        println!("val = {}", self.val);
        println!("t3 type = {}", get_type(self)); // &mut reftest::S
    }
}
```

### パターン1：実体型を使った呼び出し

```rust
fn test1() {
    let mut s = S::new(10); // mutにしないとf3が呼べない．sはS型
    s.f3(); // &mut reftest::S
    s.f2(); // &reftest::S
    s.f1(); //  reftest::S
    //s.f1(); //2回目は呼べない
}
```
このコードを実行すると，以下の結果を得る．

```
val = 11
t3 type = &mut reftrtest::S
val = 11
t2 type = &reftrtest::S
val = 11
t1 type = reftrtest::S
```
実体型である`s`を使って呼び出すと，それぞれ引数の型に応じて`self`が実体，不変参照，可変参照として呼び出される．なお，`f1`の引数は実体型`self`を受け取るため，メソッド呼び出しで所有権が移動し，2回は呼び出すことはできない．

### パターン2：参照型を使った呼び出し

```rust
fn test2() {
    let mut s = S::new(10); // mutにしないとf3が呼べない．sはS型
    let refs: &mut S = &mut s; // &mut reftest::S
    refs.f3(); // &mut reftest::S
    refs.f2(); // &reftest::S
    //refs.f1(); // SがCopyトレイトを実装していないので，デリファレンスできない
}
```
これを実行すると以下の結果を得る

```
val = 11
t3 type = &mut reftrtest::S
val = 11
t2 type = &reftrtest::S
```
参照型を使った場合でも，`f2`, `f3`の`self`はパターン1の実体型を使った場合と変化がない．ただし，`refs.f1()`の呼び出しはコンパイルエラーとなる．これは，`f1`は引数に実体を必要としているのに，参照型では所有権を移動できないためだと思われる．
こう考えると，参照型でメソッドを呼び出す意味はあまりないのかもしれない．

### パターン3: トレイトを定義して実装する
以下のトレイトTを定義し，Sに対して実装してみる

```rust
trait T {
    fn tf1(self);
    fn tf2(&self);
    fn tf3(&mut self);
}

impl T for S {
    fn tf1(self) {
        println!("tf1 val = {}", self.val);
        println!("{}", get_type(self));
    }
    fn tf2(&self) {        
        println!("tf2 val = {}", self.val);
        println!("{}", get_type(self));
    }
    fn tf3(&mut self) {        
        self.val += 1;
        println!("tf3 val = {}", self.val);
        println!("{}", get_type(self));
    }
}
```

この状態で，実体を使って呼び出してみる

```rust
fn test3() {
    println!("test3");
    let mut s = S::new(10); 
    s.tf3(); // &mut reftest::S
    s.tf2(); // &reftest::S
    s.tf1(); // reftest::S
    //s.tf1(); // 2回は呼べない
}
```
すると，以下の結果を得る

```
test3
tf3 val = 11
&mut reftrtest::S
tf2 val = 11
&reftrtest::S
tf1 val = 11
reftrtest::S
```
構造体に定義したメソッドと同様，それぞれ引数の型に応じて`self`が実体，不変参照，可変参照として呼び出されている．

### パターン4:&mut Tの実装を追加して参照型を使って呼び出し
RUSTの標準APIの実装でしばしば見受けられる，`T`の可変参照`&mut T`に対してトレイトの実装を追加する．

```rust
impl<B: T> T for &mut B {
    fn tf1(self) {
        println!("tf1 in &mut B");
        println!("--> {}", get_type(self));
        //(*self).tf1(); // コンパイルエラー
    }

    fn tf2(&self) {
        println!("tf2 in &mut B");
        println!("--> {}", get_type(self));
        (**self).tf2();
    }

    fn tf3(&mut self) {
        println!("tf3 in &mut B");
        //println!("--> {}", get_type(self)); //コメントを外すとコンパイルエラーになる
        (**self).tf3();

    }
}
```
こうした実装を最初見たときには意味がわからなかったけれど，これはある型`T`をトレイト境界に持つ任意の`B`型に対し，`&mut B`型に`T`を実装する，と読む．この説明はたぶん正しいのだけれど，具体的にどういうことか分かりづらい．パターン3で`S`に対してトレイト`T`の実装を追加した．そして`S`だけでなく`&mut S`に対してもこのトレイトを実装したい状況を考える．ここで大事なのが，`S`, `&S`, `&mut S`は**全く別の型である**ということである．したがって，`&mut S`に対してトレイト`T`の実装を加えるならば，

```rust
impl T for &mut S {...}
```
とすればいいのだが，こうすると`S`ではない他の型`P`に対してトレイト`T`を実装したとき，`&mut P`にも実装するとなると同じ記述(`imple T for &mut P`)をすることになる．そして，多くの場合，`&mut S`でも`S`と同じ振る舞いにしたい，という場合がほとんどだと思われる．そこで，

```rust
impl<B: T> T for &mut B {..}
```
と定義することで，`T`を満たす任意の型`B`の可変参照型(`&mut B`)の実装を与えることで，`S`と`P`両方の可変参照型に対して一気に実装を与えることができる(`(**self).xx()`の呼び出しがそれであるが，後述)．もちろん，個別の実装を与えたければ個別の実装を与える必要がある．

この状態で，以下のように可変参照型を使って以下のコードを実行してみる

```rust
    println!("test4");
    let mut s = S::new(10); 
    let mut rs = &mut s; // rsは&mut S
    rs.tf3(); //&mut &mut reftrtest::S
    rs.tf2(); // &&mut reftrtest::S
    rs.tf1(); // &mut reftrtest::S
```
すると，以下の結果を得る

```
tf3 val = 11
&mut reftrtest::S -->(*)ここが予想を裏切る
tf2 in &mut B
--> &&mut reftrtest::S -> 二重参照型&&
tf2 val = 11
&reftrtest::S
tf1 in &mut B
--> &mut reftrtest::S
```
これまでの実行結果と異なり，tf3の呼び出しで直感に反した振る舞いをする．`tf2`, `tf1`は`&mut B`に実装したメソッドが呼ばれているが，`tf3`は`impl T for S`で定義したものが呼び出されている．また，`tf2`の結果を見ると，`self`は`&&mut S`となっている．
つまり，参照型(`&mut s`)を使って参照型を引数に取る(`&self`)メソッド(`tf2`とか`tf3`)の中で，`self`は二重参照型となっている．よって，`&mut B`のトレイト実装で`(**self).tf2()`とかしているのは，2回デリファレンスすることで`&&B`から`B`まで戻り，`B`で定義したメソッドを呼び出してその結果を返す，という意味になる．こうすることで，任意の`B`に対し，`&mut B`にも同じ実装を与えている(絶対に分かりづらいと思うんだけどどうなんでしょう．．)．
さて，次に実体型を引数に取る`tf1`で

```rust
//(*self).tf1(); // コンパイルエラー
```
と書いた箇所があるが，`&self`が二重参照となるなら`self`は一重参照なので，呼び出せそうな気もするが，これは先の例と同じ，参照からは所有権を剥奪できないので，呼び出すことができない．
最後に，予想を裏切った`tf3`はどうすれば呼び出されるようになるかというと，

```rust
let mut s = S::new(10); 
let mut rs = &mut s; // rsは&mut S
(&mut rs).tf3(); //&mut &mut reftrtest::S --> ここ
```
こんなふうに，可変参照の`rs`をさらに可変参照でキャスト(`&mut rs`)すると呼び出されるようになる．そして，`&mut B`のトレイと実装で`self`は`&mut &mut S`となる．標準APIでこのような実装を見かけるが，こんな面倒くさい呼び出し方いつするのか不明．．暗黙のうちに行われてるのでしょうかね．．．

### パターン5:&Tの実装を追加して参照型を使って呼び出し
トレイト`T`に対し，`&mut B`の実装をしたが，`&B`の実装もできるはずである(別の型なので)．そこで，以下を追加する

```rust
impl<B: T> T for & B {
    fn tf1(self) {
        println!("tf1 in &B");
        println!("**> {}", get_type(self));
    }

    fn tf2(&self) {
        println!("tf2 in &B");
        println!("**> {}", get_type(self));
        (**self).tf2();
    } 
    
    fn tf3(&mut self) {
        println!("tf3 in &B");
        //println!("--> {}", get_type(self));
        //(**self).tf3(); // できない．&Bなのでmutじゃないから
    }
}
```
もちろん実装を与えることができるが，`tf1`ではパターン4と同様に実体型の`B`の`tf1`を呼び出すことはできない．また，`tf3`も`&B`は不変参照なので，実体型の`B`の`tf3`を呼び出すことができない．ここでできるのは`tf2`の呼び出しだけである．
以下のコードを実行する

```rust
let s = S::new(10); 
let rs = &s;
s.tf2();  --> impl T for Sが呼ばれる
println!("---");
(&rs).tf2();  --> impl T for &Bが呼ばれる
```
すると，以下の結果を得る

```
tf2 val = 10
&reftrtest::S
---
tf2 in &B
**> &&reftrtest::S
tf2 val = 10
&reftrtest::S
```
参照型(&s)で`tf2`を呼び出しても`impl T for S`の`tf2`が呼ばれる．`impl T for &B`の`tf2`を呼び出すには，`(&rs).tf2()`と参照でキャストする必要がある．

### 結論
トレイト実装を任意の型についてその参照にもまとめて与えたい場合，可変参照に対する実装には意味があるが，不変参照の実装にはあまり意味がない(気がする)．また，こんなことを毎回考えて実装する必要はたぶんなく，基本は所有権ベースで考えて実体型，参照型を選び，さらに値を書き換えるかどうかで可変参照か不変参照かを選べば良いと思う．

P.S. ~~ある`T`の可変参照`&mut T`のトレイト実装があるのは，そのオブジェクトの可変参照を引数で渡し，その中でそのオブジェクトのメソッドを呼び出すことがあるからなのかも．．と思いついたりしました．~~

### 追加
ある型`T`のトレイト実装に対し，その可変参照への実装が必要な理由を教えていただきました．
以下のように，メソッド`f1`の引数は`impl T`であり，トレイトTを実装していれば引数として与えることができる．このとき，`S`でも`&mut S`でも関係なくf1を呼び出したい．このとき，`&mut S`の実装がなければ呼び出すことができない．

```rust
fn f1(mut v: impl T) {
    v.tf3();
}

let s = S::new(20);
f1(s);
let mut s1 = S::new(30);
f1(&mut s1);

}
```

### 追記2：メソッドやトレイトのselfについて
RUSTでは，構造体に関連付ける=メソッドとして関数を定義するために`self`を用いる．そして，`self`を不変実体，不変借用，可変実体，可変借用で用いる，以下の4パターンがありうる．

```rust
struct Person {
    name: String,
    age: u32,
}

impl Person {
    // 不変実体
    fn hello(self) -> () { 
        println!("Hello");
    }
    // 不変借用
    fn hello2(&self) -> () {  
        println!("I am {} year(s) old", self.age);
    }

    // 可変実体
    fn hello3(mut self) -> () {
        println!("Hello 3");
        self.age = 100;
        println!("I am {} year(s) old", self.age);
    }

    // 可変借用
    fn hello4(&mut self ) { 
        println!("Hello 4");
        self.age = 200;
        println!("I am {} year(s) old", self.age);
    }
}

fn impl_test() {
 
    let p = Person{
        name: String::from("Taro"),
        age: 20,
    };

    p.say_name().say_age();
    p.hello();
    //p.hello();  // 2回目はエラー．-> move セマンティクス

    let mut p2 = Person::new("Jiro", 20);
    p2.hello2();
    p2.hello4();
    p2.hello3(); // mutable move
    //p2.hello4(); // moveしたのでエラー
}
```
`self`に関しては関数の引数に型を書かないが，これはシンタックスシュガーで，型まで書くと以下のようになる．

```rust
impl Person {

    fn hello(self: Self) -> () {
        println!("Hello");    
    }

    fn hello2(self: &Self) -> () {
        println!("I am {} year(s) old", self.age);
    }

    fn hello3(mut self : Self) -> () {
        println!("Hello 3");
        self.age = 100;
        println!("I am {} year(s) old", self.age);
    }
    fn hello4(self : &mut Self) { // == selfの前のmutはつけなくてもいい
        println!("Hello 4");
        self.age = 200;
        println!("I am {} year(s) old", self.age);
    }
```
型が書いてあったほうが，はっきりしていいと思う

間違い等あればご指摘ください．