今年の言語はRust その５０

# 今年の言語はRust その４９

Rustを学びます

Rustの日本語ドキュメント 2nd Edition
https://doc.rust-jp.rs/book/second-edition/

オリジナル(英語)
https://doc.rust-lang.org/book/


## 実行環境

```sh
$ cargo -V
cargo 1.33.0 (f099fe94b 2019-02-12)

$ rustup -V
rustup 1.17.0 (069c88ed6 2019-03-05)

$ rustc --version
rustc 1.33.0 (2aa4c46cf 2019-02-28)

$  cat /proc/version
Linux version 4.14.97-74.72.amzn1.x86_64 (mockbuild@gobi-build-64002) 
(gcc version 7.2.1 20170915 (Red Hat 7.2.1-2) (GCC)) 
#1 SMP Tue Feb 5 20:59:30 UTC 2019

$ uname -a
Linux ip-10-100-0-8 4.14.97-74.72.amzn1.x86_64 
#1 SMP Tue Feb 5 20:59:30 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

$ cat /etc/\*release
NAME="Amazon Linux AMI"
VERSION="2018.03"
ID="amzn"
ID_LIKE="rhel fedora"
VERSION_ID="2018.03"
PRETTY_NAME="Amazon Linux AMI 2018.03"
ANSI_COLOR="0;33"
CPE_NAME="cpe:/o:amazon:linux:2018.03:ga"
HOME_URL="http://aws.amazon.com/amazon-linux-ami/"
Amazon Linux AMI release 2018.03

```

## 17. Rustのオブジェクト指向プログラミング機能

### 17.3 オブジェクト指向パターン

__ステートパターン__ を実装してみます。



急に __take()__ が出てきてわからない。


#### __take()__

```rust
self.state.take()
```
調べると、

> オプションから値を取り除き、Noneをその場所に残す。
[Rust Option入門者向けの補足追記 - nacika.com](https://nacika.com/entry/2017/11/12/071722/)

```rust
let mut x = Some(2);
x.take();
assert_eq!(x, None);

let mut x: Option<u32> = None;
x.take();
assert_eq!(x, None);
```

#### __as_ref__

>Option を Option<&T> に変換する。この手法によって、元の値には触れず、参照のみを受け取ります。
borrow は発生しません。[Rust Option入門者向けの補足追記 - nacika.com](https://nacika.com/entry/2017/11/12/071722/)

```rust
let num_as_str: Option<String> = Some("10".to_string());
// First, cast `Option<String>` to `Option<&String>` with `as_ref`,
// then consume *that* with `map`, leaving `num_as_str` on the stack.

let num_as_int: Option<usize> = num_as_str.as_ref().map(|n| n.len());
println!("still can print num_as_str: {:?}", num_as_str);
```


src/main.rs

```rust

extern crate blog;
use blog::Post;

fn main() {
  let mut post = Post::new();

  post.add_text("I ate a salad for lunch today");
  assert_eq!("", post.content());

  post.request_review();
  assert_eq!("", post.content());

  post.approve();
  assert_eq!("I ate a salad for lunch today", post.content());

}

```

src/lib.rs

```rust

/// Post
pub struct Post {
  state: Option<Box<State>>,
  content: String,
}


impl Post {

  pub fn new() -> Post {
    Post {
      state: Some(Box::new(Draft {})),
      content: String::new(),
    }
  }

  pub fn add_text(&mut self, text: &str) {
    self.content.push_str(text);
  }


  pub fn content(&self) -> &str {
    self.state.as_ref().unwrap().content(&self)
  }


  pub fn request_review(&mut self) {
    if let Some(s) = self.state.take() {
      self.state = Some(s.request_review())
    }
  }


  pub fn approve(&mut self) {
    if let Some(s) = self.state.take() {
      self.state = Some(s.approve())
    }
  }

}


/// State
///
///! 親クラス的な
trait State {
  fn request_review(self: Box<Self>) -> Box<State>;
  fn approve(self: Box<Self>) -> Box<State>;
  fn content<'a>(&self, post: &'a Post) -> &'a str {
    ""
  }
}


/// Draft
///
///! 子クラス的な
struct Draft {}

impl State for Draft {
  fn request_review(self: Box<Self>) -> Box<State> {
    Box::new(PendingReview {})
  }

  fn approve(self: Box<Self>) -> Box<State> {
    self
  }
}


/// PendingReview
///
///! 子クラス的な
struct PendingReview {}

impl State for PendingReview {
  fn request_review(self: Box<Self>) -> Box<State> {
    self
  }

  fn approve(self: Box<Self>) -> Box<State> {
    Box::new(Published {})
  }
}


/// Published
///
///! 子クラス的な
struct Published {}

impl State for Published {
  fn request_review(self: Box<Self>) -> Box<State> {
    self
  }

  fn approve(self: Box<Self>) -> Box<State> {
    self
  }

  fn content<'a>(&self, post:&'a Post) -> &'a str{
    &post.content
  }
}

```


### 状態と振る舞いを型としてコード化する

>ステートパターンを再考して別の代償を得る方法をお見せします。状態と遷移を完全にカプセル化して、 外部のコードに知らせないようにするよりも、状態を異なる型にコード化します。結果的に、 Rustの型検査システムが、公開記事のみが許可される箇所で草稿記事の使用を試みることをコンパイルエラーを発して阻止します。


状態の数の分だけ、型(つまり、struct)を定義するやりかた。


```rust
extern crate blog;
use blog::Post;

fn main() {
  let mut post = Post::new();

  post.add_text("I ate a salad for lunch today");

  let post = post.request_review();

  let post = post.approve();


  assert_eq!("I ate a salad for lunch today", post.content());

}
```

```rust

pub struct Post {
  content: String,
}


pub struct DraftPost {
  content: String,
}


pub struct PendingReviewPost {
  content: String,
}


impl Post {
  pub fn new() -> DraftPost {
    DraftPost {
      content: String::new(),
    }
  }

  pub fn content(&self) -> &str {
    &self.content
  }
}


impl DraftPost {
  pub fn add_text(&mut self, text: &str) {
    self.content.push_str(text);
  }

  pub fn request_review(self) -> PendingReviewPost {
    PendingReviewPost {
      content: self.content,
    }
  }
}


impl PendingReviewPost {
  pub fn approve(self) -> Post {
    Post {
      content: self.content,
    }
  }
}

```


以上。

状態を管理する実装を2種類示した。

オブジェクト指向でのステートパターンと、状態の型を定義するRust風のやり方。



イエス！