第1日目 - Rust入門
Rustやるぞっ
かなり前にこの記事を読んで分からないなりに感銘を受けてから1年以上が経ち…
Re:FizzBuzzから始めるRust生活 - cats cats cats
Zennのこの素晴らしい本は半分くらいまでコード真似しながらやってみたものの,所有権と借用を理解する前に投げ出してしまい…
RustCoder ―― AtCoder と Rust で始める競技プログラミング入門
今に至る.
インストールはかなり前に済ませた.”Visual Studio C++ Build tools”が必要というところでだけやや迷う.
Getting started - Rust Programming Language
かわいー
__________________________
< Hello fellow Rustaceans! >
--------------------------
\
\
_~^~^~_
\) / o o \ (/
'_ - _'
/ '-----' \
rust-study/main.rs at main · roiban1344/rust-study
The book よむ
一旦公式の “the book” (The Rust Programming Language) 読んでみる.
https://doc.rust-lang.org/book/
1. Getting Started
1.1 Installation
もうやった.
1.2 Hello, World!
.rs
のソースファイルを作ってrustc
でコンパイル,実行する手順.
Hello, World! - The Rust Programming Language
Cargo.toml
は必須なのかと思っていた.
1.3 Hello, Cargo!
ここからcargo
使う.
$ cargo new hello_world
$ cd hello_world
$ ./target/debug/hello_world
Hello, world!
この/target/debug
下にhello_cargo.exe
とhello_cargo.pdb
ファイルが作られている.debug
? と思ったらcargo build --release
というオプションがあって,こちらでは最適化の施された実行ファイルが/target/release
に作られる.何か重い計算をやりたいときはこっちのファイルを使うといいのか.ベンチマークテストで比較してみたいな.
cargo run
コマンドでも同様に実行できて,
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target\debug\hello_cargo.exe`
Hello, world!
今回はコンパイル済みのため実行だけ.変更を加えるとコンパイルから始まる.
cargo check
はコンパイルが通るかどうかだけ検査して実行ファイルを作成しない.
2. Programming a Guessing Game
Gussing Game: 数当てゲームを作る.同じMozillaでJavaScriptのチュートリアルでやったことがある.
A first splash into JavaScript - Learn web development | MDN
Setting Up a New Project
$ cargo new guessing_game
Processing a Guess
入力を受け取る部分を書く.C++ならcin
で済むところが割と面倒になる.競技プログラミング用にはproconio
クレートというのが使われるというのを上述のRustCoderで知った.
proconio - crates.io: Rust Package Registry
std::prelude! なんて良い名付けなんだ…….String
やVec
型はここに入っているのでuse
なしで使える.
Storing Values with Variables
入力を変数guess
として受け取る.
String::new()
はstring
型オブジェクトを初期化する.new
関数は(String
型の)”associated function”といって,特定のインスタンスではなく型に備わっている.他の言語の静的メソッドに相当.日本語訳だと「関連関数」となっている.「静的」もそうだが慣れないといまいちピンとこない.
Handling Potential Failure with the Result Type
read_line
はio::Result
型を返す.これはOk
とErr
の”variants”(「列挙子」)からなる列挙型.read_line
がErr
を返した場合,.expect
メソッドの引数のメッセージを表示してプログラムが停止する.Ok
を返した場合はread_line
の結果をそのまま受け流す..expect
を付けなかった場合,コンパイルは通るが適切なエラー処理が行われていないことに関して警告が表示される.
後から気付いたメモ.「そのまま受け流す」と書いたがよく読むとそうではなくて,参照されているguess
へ文字を付け加えるということらしい.改行文字が来た場合はbreak
する.
Printing Values with println!
Placeholders
The set of curly brackets,
{}
, is a placeholder: think of{}
as little crab pincers that hold a value in place.
蟹ネタを入れてくる.それとも”crab pincers”は普通に使われる表現なのだろうか.Pythonに似たプレースホルダー付きのprint文が使える.
Testing the First Part
ここまでで書いたコードでcargo run
すると,入力した値をそのまま返すプログラムとして正しく動く.
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);
}
Guess the number!
Please input your guess.
42
You guessed: 42
Generating a Secret Number
数当ての対象になる数をランダム生成する機能を追加する.
Using a Crate to Get More Functionality
実行ファイルが入ったクレートを”binary crate”(バイナリクレート)と呼んで,他のプログラムで使われる前提のクレートを”library crate”(ライブラリクレート)と呼ぶ.ここではrand
ライブラリクレートを使うためにCargo.toml
にdependencyを追記する.
Cargo.toml
は”Semantic Versioning”(SemVer)でバージョンを解釈している.数値は”MAJOR.MINOR.PATCH”の形式で,
- MAJOR: APIの変更を含む
- MINOR: 機能は追加されるが後方互換性を保つ
- PATCH: バグの修正
を意味する."0.8.3"
は"^0.8.3"
の意味で,0.8.3以降かつ0.9.0未満の範囲で最新のバージョンが利用される.バグの修正を含む最新のバージョンが選ばれるということ.
[dependencies]
rand = "0.8.3"
cargo build
でrand
クレートがダウンロードされる.ドキュメントに
You may see different version numbers
とあるが,確かに1つパッチ修正が加わっていた.
Compiling rand v0.8.4
Crates.ioのrand
クレートのページ.
rand - crates.io: Rust Package Registry
Updating a Crate to Get a New Version
将来,依存するクレートのパッチバージョンが上がっているものの何らかの誤りでバグを含んでしまうことがあるかもしれない.もしそういった望ましくない事態が発生しても,cargo
はCargo.toml
から再計算を行うのではなく.Cargo.lock
のほうを見るので実装時の動作は保証される.Cargo.lock
は自動生成されるファイルだが,この理由でgit管理下には置くべきだということになるのか.”Dependency”の概念について講義でも受けたい気分がある.
GitHubが狙う「ライブラリのバージョン管理問題」の解決と依存関係地獄の話 - ぶるーたるごぶりん
Generating a Random Number
use rand::Rng;
でRng
(Rngrandom number genertor)トレイトの利用を宣言する.
cargo doc --open
面白い! ドキュメントが生成されて,依存するクレートの仕様を見ることができる.えー便利.
Comparing the Guess to the Secret Number
use std::cmp::Ordering;
でOrdering
という新しい列挙型を,標準ライブラリからスコープへ入れる.Less
, Greater
, Equal
の3つの列挙子からなる.
match
文は他の言語のおおよそswitch
に相当して,可能なパターンに対する網羅的な処理を記述できる.
Rustでは同名の変数を再宣言する”shadowing”が可能.型もミュータビリティも違っていてよい.
String
に対するtrim
メソッドは空白を削除する.Perlのchomp
か.
全く本筋と関係ないがこの質問でGoogle Books Ngram Viewerというのがあることを知った.
ここでtrim
を抜いて
- let guess: u32 = guess.trim().parse().expect("Please type a number!");
+ let guess: u32 = guess.parse().expect("Please type a number!");
(↑差分のシンタックスハイライティングが効いて嬉しい.Diff syntax highlighting in Github Markdown - Stack Overflow)
とするとpanicする.
Guess the number!
The secret number is: 92
Please input your guess.
42
thread 'main' panicked at 'Please type a number!: ParseIntError { kind: InvalidDigit }', src\main.rs:20:36
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\guessing_game.exe` (exit code: 101)
Allowing Multiple Guesses with Looping
loop
文で停止しないループを書ける.条件なしのwhile(true)
を書くより合理的だ.
The user could always interrupt the program by using the keyboard shortcut ctrl-c. But there’s another way to escape this insatiable monster,…
insatiable: 貪欲な,飽くことを知らない.知らなかった.
Quitting After a Correct Guess
数当て成功時にbreak
するように変更する.
- Ordering::Equal => println!("You win!"),
+ Ordering::Equal => {
+ println!("You win!");
+ break;
+ }
この書き方ができるのは気持ちが良い.if
が要らない.
これで数当てゲームが一応完成した.
Handling Invalid Input
不正な入力を無視して再入力できるようにする.
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
これもなかなか.
secret_number
のプリント文を消してゲームとして完成!
rust-study/main.rs at main · roiban1344/rust-study
TODOメモ
- Rustフォーマッターのgit hooksを入れたい.
- 一度ここまで覚えたことでfizzbuzz書く.