今年の言語は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

```


## 12 入出力プロジェクト
コマンドラインプログラムを構築する

### 12.3 リファクタリングしてモジュール性とエラー処理を向上させる

バイナリプロジェクトの開発において、__main関数__ の分割と責任を考える


- プログラムをmain.rsとlib.rsに分け、ロジックをlib.rsに移動する。
- コマンドライン引数の解析ロジックが小規模な限り、main.rsに置いても良い。
- コマンドライン引数の解析ロジックが複雑化の様相を呈し始めたら、main.rsから抽出してlib.rsに移動する。

この工程の後にmain関数に残る責任は以下に限定される:

- 引数の値でコマンドライン引数の解析ロジックを呼び出す
- 他のあらゆる設定を行う
- lib.rsのrun関数を呼び出す
- runがエラーを返した時に処理する

#### 引数解析器を抽出する

```rust

use std::env;
use std::fs::File;
use std::io::prelude::*;


fn main() {

    let args: Vec<String> = env::args().collect();
    
    let (query, filename) = parse_config(&args);
    
    println!("Searching for {}", query);
    println!("In file {}", filename);
    
    let mut f = File::open(filename).expect("file not found");
    
    let mut contents = String::new();
    f.read_to_string(&mut contents)
        .expect("something went wrong reading the file");
    
    println!("With text:\n{}", contents);
    
}


fn parse_config(args: &[String]) -> (&str, &str) {

    let query = &args[1];
    let filename = &args[2];
    
    (query, filename)
    
}
```

#### 設定値をまとめる

```rust

use std::env;
use std::fs::File;
use std::io::prelude::*;


fn main() {

    let args: Vec<String> = env::args().collect();
    
    let config = parse_config(&args);
    
    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);
    
    let mut f = File::open(config.filename).expect("file not found");
    
    let mut contents = String::new();
    f.read_to_string(&mut contents)
        .expect("something went wrong reading the file");
    
    println!("With text:\n{}", contents);
    
}

struct Config {
    query: String,
    filename: String,
}

fn parse_config(args: &[String]) -> Config {

    let query = args[1].clone();
    let filename = args[2].clone();
    
    Config {
        query,
        filename,
    }
    
}
```

>
    cloneを使用する代償

    実行時コストのためにcloneを使用して所有権問題を解消するのを避ける傾向が多くのRustaceanにあります。 第13章で、この種の状況においてより効率的なメソッドの使用法を学ぶでしょう。ですがとりあえずは、 これらのコピーをするのは1回だけですし、ファイル名とクエリ文字列は非常に小さなものなので、 いくつかの文字列をコピーして進捗するのは良しとしましょう。最初の通り道でコードを究極的に効率化しようとするよりも、 ちょっと非効率的でも動くプログラムを用意する方がいいでしょう。もっとRustの経験を積めば、 最も効率的な解決法から開始することも簡単になるでしょうが、今は、cloneを呼び出すのは完璧に受け入れられることです。


#### Configのコンストラクタを作成する

```rust

use std::env;
use std::fs::File;
use std::io::prelude::*;


fn main() {

    let args: Vec<String> = env::args().collect();
    
    let config = Config::new(&args);
    
    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);
    
    let mut f = File::open(config.filename).expect("file not found");
    
    let mut contents = String::new();
    f.read_to_string(&mut contents)
        .expect("something went wrong reading the file");
    
    println!("With text:\n{}", contents);
    
}

struct Config {
    query: String,
    filename: String,
}


impl Config {
    fn new(args: &[String]) -> Config {

        let query = args[1].clone();
        let filename = args[2].clone();
        
        Config {
            query,
            filename,
        }
        
    }
}

```

#### エラーメッセージを改善する


```Rust
// --snip--
fn new(args: &[String]) -> Config {
    if args.len() < 3 {
        // 引数の数が足りません
        panic!("not enough arguments");
    }
    // --snip--
```

#### panic!を呼び出す代わりにnewからReusltを返す

Rustっぽくなってきました

```rust
impl Config {
    fn new(args: &[String]) -> Result<Config, &'static str> {
    
        if args.len() < 3 {
            return Err("not enough arguments");
        }

        let query = args[1].clone();
        let filename = args[2].clone();
        
        Ok(Config { query, filename })
        
    }
}

```

Resultがかえってくるということは、__受け側でエラー処理__ をくみます


```rust
use std::process;

fn main() {

    let args: Vec<String> = env::args().collect();
    
    let config = Config::new(&args).unwrap_or_else(|err|{
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    
}


```

#### mainからロジックを抽出する

```Rust
fn main() {

    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);
    
    run(config);
    
}


fn run(config: Config) {

    let mut f = File::open(config.filename).expect("file not found");
    
    let mut contents = String::new();
    f.read_to_string(&mut contents)
        .expect("something went wrong reading the file");
    
    println!("With text:\n{}", contents);
}

```


#### run関数からエラーを返す


```rust
fn run(config: Config) -> Result<(), Box<Error>>{

    let mut f = File::open(config.filename)?;
        // .expect("file not found");
    
    let mut contents = String::new();
    f.read_to_string(&mut contents)?;
        // .expect("something went wrong reading the file");
    
    println!("With text:\n{}", contents);
    
    Ok(())
}

```

#### mainでrunからのエラーを処理する

```rust
fn main() {

    
    if let Err(e) = run(config) {
        
        println!("Application error: {}", e);
        
        process::exit(1);
    }
    
}


```

#### コードをライブラリクレートに分割する

```rust
extern crate minigrep;

use std::env;
use std::process;

use minigrep::Config;

fn main() {

    let args: Vec<String> = env::args().collect();
    
    let config = Config::new(&args).unwrap_or_else(|err|{
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    
    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);
    
    if let Err(e) = minigrep::run(config) {
        
        println!("Application error: {}", e);
        
        process::exit(1);
    }
    
}


```

```rust
use std::fs::File;
use std::io::prelude::*;
use std::error::Error;


pub fn run(config: Config) -> Result<(), Box<Error>>{

    let mut f = File::open(config.filename)?;
    
    let mut contents = String::new();
    f.read_to_string(&mut contents)?;
    
    println!("With text:\n{}", contents);
    
    Ok(())
}


pub struct Config {
    pub query: String,
    pub filename: String,
}


impl Config {
    pub fn new(args: &[String]) -> Result<Config, &'static str> {
    
        if args.len() < 3 {
            return Err("not enough arguments");
        }

        let query = args[1].clone();
        let filename = args[2].clone();
        
        Ok(Config { query, filename })
        
    }
}

```




Rust楽しいすね。


イエス！