Programming Rust 第2章 Rust ツアー 数合わせゲーム
https://gyazo.com/1f99bf28ff3ff357c45ccfba0dfd63e4
オライリーのほうがいきなり高次なツーリングレベルだったので少し気晴らしで通勤中読んだ
結構いろいろ重要な要素、基礎的なシンタックス・セマンティクスが詰まっていたので写経してみた
cargo new guessing_game --bin
Cargo.toml の確認
code:toml
name = "guessing_game"
version = "0.1.0"
要は package.json, Gemfile , composer.json な雑な理解
use std::io
標準ライブラリのインポート
デフォルトで暗黙的にインポートされるものはプレリュードという
use std::io::prelude::*; といった明示的な io プレリュードもある
main() 関数がプログラムのエントリポイントになります。 fn 構文は新たな関数を宣言し、 () で引数がないことを示し、 { が関数本体の始まりです。 返り値の型は書かなかったので、 () 、つまり空のタプルとして扱われます。
空のタプルとは…? 関数自体の返り値を書かないと、暗黙的に空のリスト型が返る?
let mut guess = String::new();
let 変数束縛(バインディング)の宣言文
基本デフォルトで宣言するとイミュータブル
mut修飾子をつけてミュータブルであることを明示
let は代入の左辺に単に1つの名前を取るのではなく、実際にはパターンを受け取ります
パターンとは
String::new()
Stringは伸長可能でUTF-8でエンコードされたテキスト片
String のインスタンスを作ってるわけではない
新しい空の String を作る関数の呼び出し(スタティックメソッドと言っていいかも)
::new() 新しい値をを作るのによく使われるメソッド名なのでよく見ることになるかもとのこと
特定の型に紐づく「関連関数」
io::stdin()
use した標準ライブラリをインポートした std::io の関連関数
ここでは標準入力へのハンドルを返しユーザからの入力を取得
.read_line(&mut guess)
上記ハンドルに対して呼ぶメソッド
メソッド ≒ 関連関数
メソッドは関連関数のようなものですが、型自体ではなくインスタンスに対してだけ使えます
???
引数 &mut guess
Rustには参照と呼ばれる機能があり、1つのデータに対して複数の参照を持つことができます
Rustの主要な売りの1つが、参照をいかに安全に簡単に使えるかなので、参照は複雑な機能
基本、参照もまたイミュータブルなのでミュータブルなものは宣言が必要とだけ
なぜ read_line() は文字列へのミュータブルな参照を取るのでしょうか? read_line() はユーザが標準入力に打ったものを取得し、それを文字列に格納します。 ですから、格納先の文字列を引数として受け取り、そこに入力文字列を追加するために、ミュータブルであることが求められるのです。
チェーンしている前述のメソッド io::stdin() 入力を取得して格納、その格納した文字列が引数でチェーンしたメソッドに渡ってくるところでなにか理解が止まっている(JavaScript と比較してという文脈でしかないけど
expect("Failed...")
read_line() は引数として渡した &mut guess にユーザの入力を格納して
かつ、io::Result という値も返す
io::Result には expect が定義されている
ここではメッセージ表示後に panic! = プログラムをクラッシュさせる
特例かな、エラーから復旧させたい場合は違う方法を取るのが普通
このメソッドなしでもコンパイルできるが、Resultが使われるべきとの警告が出る
https://gyazo.com/280516036432b15cd2d20c5c14a63fa7
依存クレート追加
Cargo.toml へ依存追加。手で追加?? cargo build して依存を落としてくる様子
https://gyazo.com/4e744627cbc400a1a690757c185c6225
レジストリのインデックスをアップデートしてるが結構かかった(初回だけかな
Cargo.lock も更新されこれがロックファイル
cargo update => semver でいうマイナーバージョンまでのアップデート追随
extern crate rand
依存クレートの使用を明示
use rand::Rng の1行はクレート内のメソッドを使う際にスコープないと使えないため
gen_range()
メソッドは「トレイト」と呼ばれるもので定義されており、動作させるために、該当するトレイトをスコープに入れる必要がある
rand::thread_rng()
現在の実行スレッドに対してローカルな、乱数生成器のコピーを取得しています 実行スレッドに対して生成器のコピー…日本語でおkという感じだ
その上で、gen_range() をすることができる
match 文追加
std::cmp::Ordering
cmp()メソッドは比較可能なものすべてに対して呼べるメソッド
Ordering 型の値を返す = enum 型
Ordering enum が3つのバリアントを持っており、match は
ある型の値を取って、それぞれの可能な値に対する「腕」を作れます
rand::thread_rng().gen_range(1, 101) で生成した値と String::new() した値を比較仕様として型エラー
型エラーが出た
String => u32 へシャドーイングで変換
let guess: u32 = guess.trim().parse()
もとの guess から不要文字の除去(標準入力から改行\nが入る)
parse => 文字列を何かの数値へとパース
パースできない場合の捕捉 expect
ループ
loop {} で無限ループ作成、該当する Ordering::Equal のマッチでループ抜け break を実行
また parse 失敗時に panic! で終了させないよう、戻り値のエラーをハンドルすることで処理を継続しループさせる
parseはOrderingと似たような enum 型のResultを返す
Ok, Errそれぞれのバリアントにデータが紐づく