もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

Rustやってみた。

はじめに

こんばんは。

花粉症辛くて、頭ぼーっとしてます。

先日CODEPREPの記事を上げたのですが、twitterで返事がありました。

無償提供期間は、9/30まで延長してくれているようです。

素晴らしい!ということで、せっかくなので、CODEPREPで紹介されていたRustを触ってみました。

やってみた

まずRust落としてみます。

CODEPREPの内容も、記載しますが、基本はコチラのRustの和訳を見ながら進めます。

ちなみに、この和訳はRsut 1.6の時に書かれたもののようで、現在Rustのバージョンは1.24.です。なので、少し内容的に古いものということは前提になります。

install

brewコマンドで落としました。

$ brew install rust
==> Downloading https://homebrew.bintray.com/bottles/libssh2-1.8.0.high_sierra.bottle.1.tar.gz
######################################################################## 100.0%
==> Pouring libssh2-1.8.0.high_sierra.bottle.1.tar.gz
🍺  /usr/local/Cellar/libssh2/1.8.0: 182 files, 797.4KB
==> Installing rust
==> Downloading https://homebrew.bintray.com/bottles/rust-1.24.1.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring rust-1.24.1.high_sierra.bottle.tar.gz
🍺  /usr/local/Cellar/rust/1.24.1: 6,033 files, 401.8MB

rustをインストールすると、一緒にcargoというツールもインストールされます。

cargoはrustをビルドしてくれるものでもあり、外部ライブラリを管理してくれるパッケージマネージャでもあるもののようです。

Composerみたいなもんかな?

$ cargo --version
cargo 0.25.0

プロジェクト作成

Cargoを使って、プロジェクトを作成できます。

$ cargo new sample01 --bin
     Created binary (application) `sample01` project

$ tree sample01
sample01
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files

数当てゲーム

コチラで紹介されている、数あてゲームを作ってみます。

1. src/main.rsを作成

※ Cargoでビルドするには、srcディレクトリ以下にソース群がないとエラーになります。

use std::io;

fn main() {
    println!("数字を当ててみて!");

    println!("予想地を入力してください。");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .expect("行の読み取りに失敗しました。");

    println!("あなたの予想値: {}", guess);
}

一旦動かしてみます。

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/sample01`
数字を当ててみて!
予想地を入力してください。
10
あなたの予想値: 10

動いてました。

2. randクレートをインストール

クレートというのは、Rustのパッケージの意味のようです。

要は外部ライブラリをインストールするという感じですね。

Composer install的な感じかと。

Cargo.tomlのdependencies部分を以下のように修正します。

[dependencies]
rand="0.3.0"

その後一旦ビルドします。

$ cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling libc v0.2.39                                                       
   Compiling rand v0.4.2
   Compiling rand v0.3.22
   Compiling sample01 v0.1.0 (file:///path/to/sample01)
    Finished dev [unoptimized + debuginfo] target(s) in 3.8 secs

ちなみにtomlというファイルが気になったので調べてみると、コチラの記事を発見しました。

iniファイルっぽいので読みやすいし、他にもいろんな描き方できるの便利っぽいです。

phpでもパーサがあるっぽいんで、今度使ってみます。

3. src/main.rsを修正

extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("数字を当ててみて!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    println!("秘密の数字は: {}です", secret_number);

    println!("予想値を入力してください。");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .expect("行の読み取りに失敗しました。");

    let guess: u32 = guess.trim().parse()
        .expect("文字を入力してください。");


    println!("あなたの予想値: {}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less    => println!("小さすぎます!"),
        Ordering::Greater => println!("大きすぎます!"),
        Ordering::Equal   => println!("あなたの勝ちです!"),
    }
}

実行してみます。

$ cargo run
   Compiling sample01 v0.1.0 (file:///path/to/sample01)
    Finished dev [unoptimized + debuginfo] target(s) in 0.63 secs
     Running `target/debug/sample01`
数字を当ててみて!
秘密の数字は: 36です
予想値を入力してください。
10
あなたの予想値: 10
小さすぎます!

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/sample01`
数字を当ててみて!
秘密の数字は: 35です
予想値を入力してください。
50
あなたの予想値: 50
大きすぎます!

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/sample01`
数字を当ててみて!
秘密の数字は: 66です
予想値を入力してください。
66
あなたの予想値: 66
あなたの勝ちです!

なんとなくそれっぽいですね!

4. ゲームに勝つまでゲームが続くようにする。

extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("数字を当ててみて!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    loop {
        println!("予想値を入力してください。");

        let mut guess = String::new();

        io::stdin().read_line(&mut guess)
            .expect("行の読み取りに失敗しました。");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_)  => continue
        };

        println!("あなたの予想値: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less    => println!("小さすぎます!"),
            Ordering::Greater => println!("大きすぎます!"),
            Ordering::Equal   => {
                println!("あなたの勝ちです!");
                break;
            }
        }
    }
}

実行します。

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/sample01`
数字を当ててみて!
予想値を入力してください。
20
あなたの予想値: 20
小さすぎます!
予想値を入力してください。
30
あなたの予想値: 30
小さすぎます!
予想値を入力してください。
40
あなたの予想値: 40
小さすぎます!
予想値を入力してください。
50
あなたの予想値: 50
大きすぎます!
予想値を入力してください。
45
あなたの予想値: 45
大きすぎます!
予想値を入力してください。
44
あなたの予想値: 44
大きすぎます!
予想値を入力してください。
43
あなたの予想値: 43
大きすぎます!
予想値を入力してください。
42
あなたの予想値: 42
あなたの勝ちです!

おーそれっぽいです!!

まとめ

今回CODEPREPで紹介されていたので触ってみたのですが、厳格な感じがphperの自分としては面白かったです。

あと、概念的に面白かったのが、所有権という機能でした。

そもそもRustの一押し機能?のようです。

まだ、キチンと説明できるほど、理解しているわけではないのですが、例えば、

fn main() {
  let test_1 = "Kojirock!".to_string();
  let test_2 = test_1;

  println!("name = {}", test_1);
}

とかってやって、runしてみるとエラー出ます。

$ cargo run
   Compiling sample02 v0.1.0 (file:///path/to/sample02)
warning: unused variable: `test_2`
 --> src/main.rs:3:7
  |
3 |   let test_2 = test_1;
  |       ^^^^^^
  |
  = note: #[warn(unused_variables)] on by default
  = note: to avoid this warning, consider using `_test_2` instead

error[E0382]: use of moved value: `test_1`
 --> src/main.rs:5:25
  |
3 |   let test_2 = test_1;
  |       ------ value moved here
4 |   
5 |   println!("name = {}", test_1);
  |                         ^^^^^^ value used here after move
  |
  = note: move occurs because `test_1` has type `std::string::String`, which does not implement the `Copy` trait

error: aborting due to previous error

error: Could not compile `sample02`.

warningの方は使ってない変数 test_2があるよってことなのですが、もう一つのほうが問題です。

use of moved value: `test_1` 

イコールで代入した場合、そのデータ(リソース)の所有権が代入先に移り、代入元はアクセスできなくなってしまうということのようです。

これを所有権の移動(ムーブ)と言います。

おもしろい概念ですよね。

一回代入(変数の移動)したんなら、使わないよね?っていう潔さというか、清潔さというか。

回避策として、Copyトレイトとかを使用すればいいようです。

コンパイルエラー時の文章で書かれてますね。これも新設でわかりやすいです。

コピーといえば、ちょっと違いますがpythonのリストのコピーとかも少しにてますよね。

元のものを使いたい場合はcopyモジュール使いなさいよとか。

こう考えるとphpってやっぱりゆるくて、入りやすいってことなのかな。

終わりに

最近当初の勉強意欲がなくなってきているのを、感じまくっています。。。

テンションの停滞期とでも言うんでしょうか。

こういう時勉強を続けている人たちにも、大小あれ必ず訪れると思うんですが、その時の乗り越え方とかを教えてほしいです。

とりあえず僕の考えた乗り越え方は、簡単なことからやってく

ということです。

最近平日に1記事、土日に1記事という感じで進めてみようと思ってたんですが、早速この作戦が裏目に出ています。

土日はやはり子供と奥さんのために使わないと、家庭環境がギクシャクしてしまいます。

なので、来週からは、頑張って平日中に2記事仕上げようと思います。

なんか記事書くことが目標みたいですが、それでもいいんです。

今年1年はとりあえず、書くこと。

来年からは内容を充実させることを目標にしてるので。

では来週も頑張ります。

次は、Rustの所有権周りについて、勉強して記事に残しておこうと思います。