はじめに
前々から気になっていたRustを折角の休みだし触ってみることにしました!
タイトル通りインストールからいけるとこまでやってみました!
学習の参考やスケジュールの目処になれば幸いです。
ちなみに筆者はまともにこれ書けるぜ!ってレベルの言語は一個もないですw
Lambdaでなんちゃってクソコード書くのが精一杯のレベルなので。。
Rustとは?
公式ドキュメント : https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/
日本語対応しているので嬉しいですね!ざっと知っていた知識としては以下です。
-
- 比較的新しめの言語
-
- C/C++と比べて安全な設計をした言語
-
- C/C++と同等の処理速度を発揮
-
- C/C++の代替えを目的として発足
-
- 静的型付けのコンパイラ言語
- Mozillaが推している言語
色々調べてわかったこと
-
- 標準でパッケージマネージャー/テスト機能が付いている
-
- 型の種類がめちゃくちゃ豊富
-
- 関数内部での型推論がイケてる!
-
- 静的に変数の寿命がわかる
-
- 自動でメモリを解放してくれる
-
- デフォルトでスレッドセーフが担保されている変数
-
- ムーブセマンティクスを言語レベルで利用可能
- StackOverflowの「最も愛されているプログラミング言語」で1位を4年連続獲得している!(2016年/2017年/2018年/2019年)
C/C++を引き継いでいるので個人的にはマクロをこねこねしていた時代を思い出して、懐かしい気持ちになりながら楽しく学べました!(嫌いな方はすごく嫌いかもしれないですw)
fn addition<'a>(a:i32,b:i32)->&'a i32{
let c=a+b;
return &c;
}
では早速やってきます!
インストール
インストールはすごく簡単です!
以下公式ページのトップからコマンドラインにコピペで終わりですw
https://www.rust-lang.org/tools/install
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
info: downloading installer
Welcome to Rust!
This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.
It will add the cargo, rustc, rustup and other commands to
Cargo's bin directory, located at:
/Users/user/.cargo/bin
This can be modified with the CARGO_HOME environment variable.
Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:
/Users/user/.rustup
This can be modified with the RUSTUP_HOME environment variable.
This path will then be added to your PATH environment variable by
modifying the profile files located at:
/Users/user/.profile
/Users/user/.bash_profile
You can uninstall at any time with rustup self uninstall and
these changes will be reverted.
Current installation options:
default host triple: x86_64-apple-darwin
default toolchain: stable
profile: default
modify PATH variable: yes
1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>1
info: profile set to 'default'
info: default host triple is x86_64-apple-darwin
info: syncing channel updates for 'stable-x86_64-apple-darwin'
info: latest update on 2019-12-19, rust version 1.40.0 (73528e339 2019-12-16)
info: downloading component 'cargo'
3.7 MiB / 3.7 MiB (100 %) 3.7 MiB/s in 1s ETA: 0s
info: downloading component 'clippy'
info: downloading component 'rust-docs'
11.9 MiB / 11.9 MiB (100 %) 3.5 MiB/s in 3s ETA: 0s
info: downloading component 'rust-std'
16.2 MiB / 16.2 MiB (100 %) 3.7 MiB/s in 4s ETA: 0s
info: downloading component 'rustc'
54.9 MiB / 54.9 MiB (100 %) 3.5 MiB/s in 15s ETA: 0s
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
11.9 MiB / 11.9 MiB (100 %) 2.6 MiB/s in 4s ETA: 0s
info: installing component 'rust-std'
16.2 MiB / 16.2 MiB (100 %) 11.2 MiB/s in 1s ETA: 0s
info: installing component 'rustc'
54.9 MiB / 54.9 MiB (100 %) 14.6 MiB/s in 3s ETA: 0s
info: installing component 'rustfmt'
info: default toolchain set to 'stable'
stable installed - rustc 1.40.0 (73528e339 2019-12-16)
Rust is installed now. Great!
To get started you need Cargo's bin directory ($HOME/.cargo/bin) in your PATH
environment variable. Next time you log in this will be done
automatically.
To configure your current shell run source $HOME/.cargo/env
パスも追加してくれているみたいなのでsource ~/.bash_profileですぐにバージョン確認ができます。
$ source ~/.bash_profile
$
$ rustc --version
rustc 1.40.0 (73528e339 2019-12-16)
$
$ cargo --version
cargo 1.40.0 (bc8e4c8be 2019-11-22)
$
$ rustdoc --version
rustdoc 1.40.0 (73528e339 2019-12-16)
ではHelloworldまで一気にやっちゃいましょう。
$ mkdir hello_world;cd hello_world
$
$ touch main.rs
$
$ ll
total 0
drwxr-xr-x 3 user staff 96 12 30 11:08 .
drwxr-xr-x 4 user staff 128 12 30 11:08 ..
-rw-r--r-- 1 user staff 0 12 30 11:08 main.rs
$
fn main() {
println!("Hello, world!");
}
$ rustc main.rs
$
$ ls -l
total 552
drwxr-xr-x 4 user staff 128 12 30 11:09 .
drwxr-xr-x 4 user staff 128 12 30 11:08 ..
-rwxr-xr-x 1 user staff 276832 12 30 11:09 main
-rw-r--r-- 1 user staff 45 12 30 11:09 main.rs
$ ./main
Hello, world!
簡単ですね!ちなみに下記のような方法でも可能です。
$ mkdir cargo_new;cd cargo_new
$ cargo new helloworld
Created binary (application) `helloworld` package
$
$ ls -l
total 0
drwxr-xr-x 3 user staff 96 12 30 11:12 .
drwxr-xr-x 6 user staff 192 12 30 11:12 ..
drwxr-xr-x 4 user staff 128 12 30 11:12 helloworld
$
$ cd helloworld
$
$ ls -l
total 8
drwxr-xr-x 4 user staff 128 12 30 11:12 .
drwxr-xr-x 3 user staff 96 12 30 11:12 ..
-rw-r--r-- 1 user staff 233 12 30 11:12 Cargo.toml
drwxr-xr-x 3 user staff 96 12 30 11:12 src
$
$ ll src
total 8
drwxr-xr-x 3 user staff 96 12 30 11:12 .
drwxr-xr-x 6 user staff 192 12 30 11:13 ..
-rw-r--r-- 1 user staff 45 12 30 11:12 main.rs
fn main() {
println!("Hello, world!");
}
$ cargo run
Compiling helloworld v0.1.0 (/Users/user/cargo_new/helloworld)
Finished dev [unoptimized + debuginfo] target(s) in 3.21s
Running `target/debug/helloworld`
Hello, world! #Hello, world!が表示される
$
$ ls -l
total 16
drwxr-xr-x 6 user staff 192 12 30 11:13 .
drwxr-xr-x 3 user staff 96 12 30 11:12 ..
-rw-r--r-- 1 user staff 142 12 30 11:13 Cargo.lock #.lockファイルが生成される
-rw-r--r-- 1 user staff 233 12 30 11:12 Cargo.toml
drwxr-xr-x 3 user staff 96 12 30 11:12 src
drwxr-xr-x 4 user staff 128 12 30 11:13 target
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "helloworld"
version = "0.1.0"
Cargo.tomlリファレンス
$ tree -d
.
├── src
└── target
└── debug
├── build
├── deps
│ └── helloworld-f52023378caf7043.dSYM
│ └── Contents
│ └── Resources
│ └── DWARF
├── examples
├── helloworld.dSYM -> deps/helloworld-f52023378caf7043.dSYM
└── incremental
└── helloworld-iqi0rwk4kl00
└── s-fj8ked5lur-u8cgl5-1x8ryuh7rlm9x
Hello worldくらいならファイル作成してしまった方が楽ですが、テンプレートが生成できるということは何か役に立つのでしょう!楽しみにして進めていきます。
main関数
main関数は特別で、全てのRustプログラムの開始点になります。
と記載があるとおりmain関数はどこにあろうとまずは実行されることに留意しなければいけません。
上からドバーっと実行されるわけではなくmainが開始点になるということを忘れてしまうと思わぬ動作を産んでしまうので注意しましょう。(その前にコンパイルで怒られることがほとんだと思いますが…)
fn main() {
hello("hello, world from function");
}
fn hello(message: &str) {
println!("{}",message);
}
数当てゲーム
公式リファレンスに倣って数当てゲームを作ってみます。
まずはcargo newを利用して必要なファイルを作成していきます。
$
uluru514:into-rust user$ cargo new guessing_game --bin
Created binary (application) `guessing_game` package
$
$ cd guessing_game
$
$ ll
total 16
drwxr-xr-x 6 user staff 192 12 30 13:03 .
drwxr-xr-x 8 user staff 256 12 30 13:03 ..
-rw-r--r-- 1 user staff 236 12 30 13:03 Cargo.toml
drwxr-xr-x 3 user staff 96 12 30 13:03 src
できたsrc/main.rsを下記に書き換えます。
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
そしたら実際にcargo runして動くことを確認してみます。
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/guessing_game`
Guess the number!
Please input your guess.
1
You guessed: 1
ちゃんと動きました。
中身を見ていきましょう。
use(ライブラリ追加)
use std::io;
// JSやPythonで言うところの import
Rustの標準で読み込まれるデフォルトのライブラリをプレリュードと言いますが、あまり多くのライブラリが含まれていないため大抵はuseを使ってインポートが必要です。
mut(変数の可変化)
let mut guess = String::new();
通常JavaScriptなどletを使う場合、変数として扱われるためデフォルトで後から値を変更することが可能ですが、Rustの場合letを宣言していてもデフォルトではイミュータブル(不変)な変数として扱われるため下記のようなコードはエラーとして扱われます。
fn main() {
let message = "immutable strings";
message = "change value";
println!("{}", message)
}
結果はこうなります。
$ cargo run
Compiling hello_world v0.1.0 (/Users/user/demo-rust/hello_world)
error[E0384]: cannot assign twice to immutable variable `message`
--> src/main.rs:3:5
|
2 | let message = "immutable strings";
| -------
| |
| first assignment to `message`
| help: make this binding mutable: `mut message` //ミュータブルな変数にするように言われる
3 | message = "change value";
| ^^^^^^^^^^^^^^^^^^^^^^^^ cannot assign twice to immutable variable //イミュータブルな変数だと怒られる
error: aborting due to previous error
For more information about this error, try `rustc --explain E0384`.
error: could not compile `hello_world`.
To learn more, run the command again with --verbose.
これをlet messageからlet mut messageに変更してあげることで通るようになります。
fn main() {
let mut message = "immutable strings";
message = "change value";
println!("{}", message)
}
$ cargo run
Compiling hello_world v0.1.0 (/Users/user/demo-rust/hello_world)
Finished dev [unoptimized + debuginfo] target(s) in 0.85s
Running `target/debug/hello_world`
change value
このように変数を扱う際は、それが不変であるか否かの判断が必要です。
メソッドの利用
今度はこちらのコードの処理を追っていきましょう。
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
上記コードの中でメソッドを呼び出しています。
io::stdin()についてはuse std::io;の関数なのでstd::io::stdinとして呼び出すことも可能です。
std::io::stdin().read_line(&mut guess)
.expect("Failed to read line");
また.read_lineはstd::io::stdinに含まれるメソッドです。
.read_line(&mut guess)となっており、let mut guess = String::new();からの文字列の入力を待ち受けています。
メソッドに関してはインスタンス内でのみ利用が可能となっています。
.expect();はエラーハンドリングに関するstd::result::Resultのメソッドでpanic!の発生を防止しています。
このあたりはmutは少し特殊ですが、Rust独特というよりも静的言語っぽいなっていう感じです。
秘密の数を生成する
Rustの標準ライブラリには乱数がないので、randクレートを利用して乱数を生成できるよう追加します。
Cargo.toml
標準の場合
[package]
name = "guessing_game"
version = "0.1.0"
authors = ["hoge-fuga <hogehoge@fuga.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
こちらに下記コマンドを実施して依存関係を追加してあげます。
dpd='[dependencies]
rand="0.3.0"'
echo "$dpd" >> Cargo.toml
[package]
name = "guessing_game"
version = "0.1.0"
authors = ["hoge-fuga <hogehoge@fuga.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand="0.3.0"
ちなみに[dependencies]のバージョン表記は様々あります。
ではパッケージをインストールするためcargo buildを実施します。
$ cargo build
Updating crates.io index
Downloaded rand v0.3.23
Downloaded rand v0.4.6
Downloaded libc v0.2.66
Compiling libc v0.2.66
Compiling rand v0.4.6
Compiling rand v0.3.23
Compiling guessing_game v0.1.0 (/Users/user/demo-rust/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 1m 10s
完了するとCargo.lockファイルも更新がかかります。
# It is not intended for manual editing.
[[package]]
name = “fuchsia-cprng”
version = “0.1.1”
source = “registry+https://github.com/rust-lang/crates.io-index”
[[package]]
name = “guessing_game”
version = “0.1.0”
dependencies = [
“rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)”,
]
[[package]]
name = “libc”
version = “0.2.66”
source = “registry+https://github.com/rust-lang/crates.io-index”
[[package]]
name = “rand”
version = “0.3.23”
source = “registry+https://github.com/rust-lang/crates.io-index”
dependencies = [
“libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)”,
“rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)”,
]
[[package]]
name = “rand”
version = “0.4.6”
source = “registry+https://github.com/rust-lang/crates.io-index”
dependencies = [
“fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)”,
“libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)”,
“rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)”,
“rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)”,
“winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)”,
]
[[package]]
name = “rand_core”
version = “0.3.1”
source = “registry+https://github.com/rust-lang/crates.io-index”
dependencies = [
“rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)”,
]
[[package]]
name = “rand_core”
version = “0.4.2”
source = “registry+https://github.com/rust-lang/crates.io-index”
[[package]]
name = “rdrand”
version = “0.4.0”
source = “registry+https://github.com/rust-lang/crates.io-index”
dependencies = [
“rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)”,
]
[[package]]
name = “winapi”
version = “0.3.8”
source = “registry+https://github.com/rust-lang/crates.io-index”
dependencies = [
“winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)”,
“winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)”,
]
[[package]]
name = “winapi-i686-pc-windows-gnu”
version = “0.4.0”
source = “registry+https://github.com/rust-lang/crates.io-index”
[[package]]
name = “winapi-x86_64-pc-windows-gnu”
version = “0.4.0”
source = “registry+https://github.com/rust-lang/crates.io-index”
[metadata]
“checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)” = “a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba”
“checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)” = “d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558”
“checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)” = “64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c”
“checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)” = “552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293”
“checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)” = “7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b”
“checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)” = “9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc”
“checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)” = “678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2”
“checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)” = “8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6”
“checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)” = “ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6”
“checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)” = “712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f”
乱数に必要なrandのインストールも完了したので利用するようにコードを書き換えます。
extern crate rand;
use std::io;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
実行結果としては以下になります。
$ cargo run
Compiling guessing_game v0.1.0 (/Users/user/demo-rust/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 24
Please input your guess.
3
You guessed: 3
こちらも分解して見ていきましょう。
トレイト
トレイトとは?
トレイトはある型が提供しなければならない機能をRustのコンパイラに伝える言語機能です。
わかるような、わからないような…tsで言うところの*.d.ts的な?
もう少しドキュメントを読み込んでみます。
traitはメソッドを集めたものであり、traitを型に実装することによって、その型の値がメソッドを提供することができるようになります。
実際に下記例をとって見るとtrait HasArea {}は関数areaがプリミティブ型f64で戻ってくること以外は記載がありません。
その下にメソッドとしてimpl トレイト for アイテムが定義されており、
スタック上の値(self)としてradiusを受け取り乗算し、その結果をさらにπ(std::f64::consts::PI)で乗算する円の面積を求める計算式になっていることがわかります。
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)
}
}
上記でトレイトは定義できましたが、実際に利用する際には境界という考え方が重要になってきます。
境界(Bounds)とはある1つの型または トレイト における制約のことです。例えば、ある関数がとる引数に境界が設定されたとすると、その関数に渡される型は設定された制約に必ず従わなければなりません。
公式ドキュメント引用
ざっくりといってしまうと、型がどのトレイトを使うのか?を明示的にしてあげる必要があります。
以下2例でトレイト境界を宣言していますが、上はエラーになり下はHasAreaを利用するよう明示しているため利用が可能になります。
// Error pattern
fn print_area<T>(shape: T) { // T -> トレイトを指定していないためareaメソッドが実装されていない扱いになる
println!("This shape has an area of {}", shape.area());
}
// Success pattern
fn print_area<T: HasArea>(shape: T) {
println!("This shape has an area of {}", shape.area());
}
これを踏まえて実際のコードに反映すると、
trait HasArea {
fn area(&self) -> f64;
}
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl HasArea for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
fn print_area<T: HasArea>(shape: T) {
println!("This shape has an area of {}", shape.area());
}
fn main() {
let c = Circle {
x: 0.0f64,
y: 0.0f64,
radius: 1.0f64,
};
print_area(c);
}
$ cargo run
Compiling hello_world v0.1.0 (/Users/user/demo-rust/hello_world)
Finished dev [unoptimized + debuginfo] target(s) in 0.33s
Running `target/debug/hello_world`
This shape has an area of 3.141592653589793
となり、areaメソッドで定義されているπ * ( 1.0 * 1.0 )が実行されている。
長くなりましたが、use rang::Rng;を冒頭で追加しているのは.gen_rangeメソッドを利用するのにRngトレイトを実装してあげる必要があるからです。
fn gen_range<T: SampleUniform, B1, B2>(&mut self, low: B1, high: B2) -> T
where
B1: SampleBorrow<T> + Sized,
B2: SampleBorrow<T> + Sized,
{
T::Sampler::sample_single(low, high, self)
}
予想値と比較する
ここまででユーザー入力と乱数生成が可能になりました。
それらを比較するような機能を実装していきます。
全体のコードはこちらです。
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
use std::cmp::Ordering;のライブラリを利用して比較を実施しています。
実行結果は下記です。
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 60
Please input your guess.
3
You guessed: 3
Too small!
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/guessing_game`
Guess the number!
The secret number is: 2
Please input your guess.
2
You guessed: 2
You win!
このように比較が行われるようになりました。
こちらも分解して見ていきましょう。
parse
比較するにあたって文字コードを揃えてあげる必要があります。
今までのコードだとguess = String::new();なので型推論から文字列と判断されてしまうためsecret_numberとの比較ができません。
なので下記2行を用いて、guessを数値へと変換してあげる必要があります。
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
let guess: u32によってguessのタイプがu32であることが明示的にわかります。
.parse()メソッドによって文字列が数値へと変換されるのですが、上記でu32に指定されているため符号なし32bit整数に変換されます。
これで後続のsecret_numberとの比較が可能になります。
シャドーイング
上記の場合let mut guess = String::new();で定義しており、guessはミュータブルな変数として扱わるため再度let guess: u32 = guess.trim().parse()で定義し直す(以前定義したものを隠す)事ができます。これにより、別の変数を用いて変換することができます。
ループ
loop{}を利用することで処理を繰り返す事ができます。
これを利用してsecret_numberと入力値が一致するまでのロジックを実装していきます。
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
loop { // 当たるまで繰り替えす処理内容
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break; // 勝った場合の戻り値をトリガーとしてloop処理を終了させる
}
}
}
}
break;を利用して繰り返し処理を抜けるのはCやC++は当然ですが、bashなんかでも利用するので馴染みがあると思います。
また以下の一文を追加して文字列を入力した際に以上終了せず、繰り返し処理を実行するようにしていきます。
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
一通りのロジックは完成しましたが、これは数字当てゲームなのでsecret_numberが見えていては意味がありませんのでこちらを表示しないよう不要な処理を消してしまいましょう。
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
// println!("The secret number is: {}", secret_number); // secret_numberを表示してしまう不要な処理
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue, // 文字列の場合繰り返す
};
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
実行結果は下記の通りです。
$ cargo run
Compiling guessing_game v0.1.0 (/Users/user/demo-rust/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.54s
Running `target/debug/guessing_game`
Guess the number!
Please input your guess.
2
You guessed: 2
Too small!
Please input your guess.
90
You guessed: 90
Too big!
Please input your guess.
test
Please input your guess.
43
You guessed: 43
You win!
これで数字当てゲームは一通り終わりです。
まだまだチュートリアルはありますが、今回は一旦ここまでにしておきます。
所感
個人的にはすごく楽しかったです!
静的言語の方が好きなのもありますが、なんとなく馴染みやすかったです。
あとはcargoが便利なのもあり、buildが非常に楽でよかったです!(CとかC++でmakefile頑張ってたのが嘘みたい。。)
ただだらーっとコード書いていくだけであれば、5分とかで終わると思いますが所有権やトレイトなどの概念を理解するのにドキュメントを飛び飛びで追っていったので3時間くらいかかりました。。
しかし導入はすごく簡単ですし他の言語に慣れている方なら割とスッと入れると思いますので、是非みなさんも一度触れてみてはいかがでしょうか?