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.exehello_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! なんて良い名付けなんだ…….StringVec型はここに入っているのでuseなしで使える.

Storing Values with Variables

入力を変数guessとして受け取る.

String::new()string型オブジェクトを初期化する.new関数は(String型の)”associated function”といって,特定のインスタンスではなく型に備わっている.他の言語の静的メソッドに相当.日本語訳だと「関連関数」となっている.「静的」もそうだが慣れないといまいちピンとこない.

Handling Potential Failure with the Result Type

read_lineio::Result型を返す.これはOkErrの”variants”(「列挙子」)からなる列挙型.read_lineErrを返した場合,.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 buildrandクレートがダウンロードされる.ドキュメントに

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

将来,依存するクレートのパッチバージョンが上がっているものの何らかの誤りでバグを含んでしまうことがあるかもしれない.もしそういった望ましくない事態が発生しても,cargoCargo.tomlから再計算を行うのではなく.Cargo.lockのほうを見るので実装時の動作は保証される.Cargo.lockは自動生成されるファイルだが,この理由でgit管理下には置くべきだということになるのか.”Dependency”の概念について講義でも受けたい気分がある.

Dependency hell - Wikipedia

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というのがあることを知った.

sentence construction - “a comparison between A and B” or “a comparison of A and B”? - English Language Learners Stack Exchange

ここで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書く.