一つの例でRustのトレイトとクロージャを完全理解（大幅誇張）する(1)

## 対象読者

- C言語のザックリとして文法くらいはわかる
- オブジェクト指向な言語を何か触ったことがある


## 例

```rust
fn map<F, T, A>(option: Option<T>, f: F) -> Option<A> where F: FnOnce(T) -> A {
    match option {
        None => None,
        Some(value) => Some(f(value)),
    }
}
```

　Rustは難しい言語と言われます。GoやJavaは他のオブジェクト指向をやってくればすんなりと理解できるのに対し、Rustはモダンな言語仕様が多く、所有権などの特有の概念があるため難しいと言われています。このコードをみて最初全く理解できなかったので言語化します。

　この関数はOption型のメソッドとして定義されています。

## ジェネリクス
　下に書いたのはジェネリクスパラメーターをを受け取る関数です。

```rust
fn takes_anything<T>(x: T) {
  // xで何か行う
}
```

 \<T> は「この関数は1つの型、 T に対してジェネリックである」ということであり、 x: T は「xは T 型である」という意味です。Tにはi64をいれてもいいし、stringをいれてもいいということです。

```rust
fn takes_two_things<T, U>(x: T, y: U) {
    // ...
}
```

もちろん二つ取ることもできます。二つ取ると上の例ではTにstring、Uにi64と別の型を代入できるようになります。


ここで例に戻ると、

```rust
fn map<F, T, A>(option: Option<T>, f: F) -> Option<A> where F: FnOnce(T) -> A {
    match option {
        None => None,
        Some(value) => Some(f(value)),
    }
}
```

\<F, T, A>はジェネリックパラメータであることがわかります。

## トレイト

　さて、ジェネリックパラメータがどんな型でも受け入れるなら区別する必要なくないですか？実はどんな型でも受け入れてしまうと、足し算をする関数に文字列を渡されるようなときに困ります。そこで、トレイト境界を用いて受け入れる型を制限します。その前にトレイトについて説明しましょう。まずは次を見てください。

```rust
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius) // ;がないときはreturn文と同じ意味になります
    }
}
```

implはオブジェクト指向言語のメソッドやメンバ関数に近い役割を持っています。そして、ここにトレイトを加えてみましょう

```rust
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

trait HasArea {
    fn area(&self) -> f64;
}

impl HasArea for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}
```

HasAreaは指定された型が面積を持っているということです。二次元図形であれば面積を持っているので、次のように

```Rust
struct Square {
    x: f64,
    y: f64,
    side: f64,
}

impl HasArea for Square {
    fn area(&self) -> f64 {
        self.side * self.side
    }
}
```

正方形にも同じような実装を施すことができます。ここで、二つの図形の面積を足し合わせる関数を作ってみましょう。

```rust
fn add_two_figures<T, F>(shape1: T, shape2: F)-> f64{
    shape1.area() + shape2.area()
}
```

しかしこれでは怒られます

```
error[E0599]: no method named `area` found for type `T` in the current scope
  --> main.rs:30:12
   |
30 |     shape1.area() + shape2.area()
   |            ^^^^
   |
```

Tには数値でも文字列でもなんでも受け取ってしまうので、数値を代入されるとお手上げです。そこで、トレイト境界を追加します。

```rust
fn add_two_figures<T : HasArea, F : HasArea>(shape1: T, shape2: F)-> f64{
    shape1.area() + shape2.area()
}
```

こうすれば。HasAreaトレイトを持つCircleとSquareしか受け入れられないので、正しくコンパイルされます。さて、ここで今までのソースコードをまとめた例を載せましょう

```rust
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

trait HasArea {
    fn area(&self) -> f64;
}

impl HasArea for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

struct Square {
    x: f64,
    y: f64,
    side: f64,
}

impl HasArea for Square {
    fn area(&self) -> f64 {
        self.side * self.side
    }
}

fn add_two_figures<T : HasArea, F : HasArea>(shape1: T, shape2: F)-> f64{
    shape1.area() + shape2.area()
}

fn main(){
    let circle = Circle{x: 1.0, y: 1.0, radius: 1.0};
    let square = Square{x: 1.0, y: 1.0, side: 1.0};
    println!("二つの図形の面積の和は{}", add_two_figures(circle ,square ));
}

// RUSULT
// 二つの図形の面積の和は4.141592653589793
```

当たり前ですが、面積を持たない型を代入するとコンパイルエラーになります。

```rust
add_two_figures(5, 10);

// ERROR
// error: the trait `HasArea` is not implemented for the type `_` [E0277]
```

また、このトレイト境界は{の直前にwhere句で表現することができます。二つの関数は同じ意味です。

```rust
fn add_two_figures<T : HasArea, F : HasArea>(shape1: T, shape2: F)-> f64{
    shape1.area() + shape2.area()
}

fn add_two_figures<T, F>(shape1: T, shape2: F)-> f64 where T: HasArea, F: HasArea {
    shape1.area() + shape2.area()
}
```

ここで例に戻りましょう

```rust
fn map<F, T, A>(option: Option<T>, f: F) -> Option<A> where F: FnOnce(T) -> A {
    match option {
        None => None,
        Some(value) => Some(f(value)),
    }
}
```

どうもジェネリックパラメーターのFとAに関しては、「FnOnce(T) -> A」という制約がついていそうですね。

次は「FnOnce(T) -> A」周りの理解を深めるため、クロージャについての説明を行います