🦀

Rustlings をやってみた: セットアップから variables 終了まで

2022/03/25に公開

業務委託で Rust を書くことになったので爆速キャッチアップを試みる.

ちなみに,本記事執筆開始時点で筆者の Rust 経験値は 0 であることを申し添えておく.

事前情報

macOS で動かしています.

先行記事の紹介

Google でヒットしたものから:

https://blog.takuchalle.dev/post/2020/12/05/rustlings/

https://p0ny.hatenablog.com/entry/2020/03/22/232651

https://makibishi.ninja/rustlings/

インストール

https://github.com/rust-lang/rustlings/

に従ってコマンドラインインストール:

curl -L https://git.io/install-rustlings | bash

コンパイルにはそれなりに時間かかる.

インストール完了後の作業

バージョン確認

rustlings コマンドが使えるようになっている:

rustlings -v
# => v4.6.0

インストール先ディレクトリの確認

デフォルトではホームディレクトリ直下:

ls ~/rustlings
# => CHANGELOG.md    Cargo.lock      LICENSE         default_out.txt info.toml       install.sh      target
# => CONTRIBUTING.md Cargo.toml      README.md       exercises       install.ps1     src             tests

まずやること: rustlings watch

インストール先ディレクトリ直下でこれを実行すると,rustlings/exercises 以下の exercise に対してビルドプロセスが走る:

pwd
# => /Users/kentarosugimoto/rustlings
rustlings watch
実行結果
⚠️  Compiling of exercises/variables/variables1.rs failed! Please try again. Here's the output:
error[E0425]: cannot find value `x` in this scope
  --> exercises/variables/variables1.rs:12:5
   |
12 |     x = 5;
   |     ^ not found in this scope

error[E0425]: cannot find value `x` in this scope
  --> exercises/variables/variables1.rs:13:36
   |
13 |     println!("x has the value {}", x);
   |                                    ^ not found in this scope

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0425`.

Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

exercises/variables/variables1.rs のビルドに失敗したことが分かる.

実際,variableshttps://github.com/rust-lang/rustlings/blob/main/exercises/README.md にもあるように初手の exercise トピックである.

というわけで,早速ビルドエラーを解消しにかかる.

Debugging variables1.rs

まずは variables1.rs の内容を覗き見:

# bat: https://github.com/sharkdp/bat
bat ./exercises/variables/variables1.rs
// variables1.rs
// Make me compile! Execute the command `rustlings hint variables1` if you want a hint :)

// About this `I AM NOT DONE` thing:
// We sometimes encourage you to keep trying things on a given exercise,
// even after you already figured it out. If you got everything working and
// feel ready for the next exercise, remove the `I AM NOT DONE` comment below.

// I AM NOT DONE

fn main() {
    x = 5;
    println!("x has the value {}", x);
}

パッと見た感じ,関数 main があるだけで,その内部スコープで宣言(?)されている x という変数が正しくバインドされていないように感じる.

https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html

の前半に書いてあることをサッと拾うと

  • 変数宣言には let キーワードが必要
  • 宣言された変数はデフォルトでは immutable 扱い
    • mutable にするためには mut キーワードを付加して let mut とする

らしいので,必要そうな変更は 12 行目に let を書き加えるだけ.

適当に vim で編集して再度 rustlings watch を叩くと,下記のようなメッセージが出る:

✅ Successfully ran exercises/variables/variables1.rs!

🎉 🎉  The code is compiling! 🎉 🎉

Output:
====================
x has the value 5

====================

You can keep working on this exercise,
or jump into the next one by removing the `I AM NOT DONE` comment:

 7 |  // feel ready for the next exercise, remove the `I AM NOT DONE` comment below.
 8 |
 9 |  // I AM NOT DONE
10 |
11 |  fn main() {
Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

テスト駆動開発(TDD)と似たようなリズムで演習問題を解けるのは良い:

  1. レッド: rustlings watch(修正前)
  2. グリーン: rustlings watch(修正後)
  3. リファクタリング: I AM NOT DONE の除去

というわけで,I AM NOT DONE を除去し,再び rustlings watch を実行すると,今度は variables1.rs の実行に成功した旨に加えて,variables2.rs のコンパイルに失敗した旨が出力される:

✅ Successfully ran exercises/variables/variables1.rs!
⚠️  Compiling of exercises/variables/variables2.rs failed! Please try again. Here's the output:
error[E0282]: type annotations needed
 --> exercises/variables/variables2.rs:7:9
  |
7 |     let x;
  |         ^ consider giving `x` a type

error: aborting due to previous error

For more information about this error, try `rustc --explain E0282`.

Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

Debugging variables2.rs

今度は,型に関するエラー.

というわけで,はたまた内容を覗き見:

bat ./exercises/variables/variables2.rs
// variables2.rs
// Make me compile! Execute the command `rustlings hint variables2` if you want a hint :)

// I AM NOT DONE

fn main() {
    let x;
    if x == 10 {
        println!("Ten!");
    } else {
        println!("Not ten!");
    }
}

以下,https://doc.rust-lang.org/book/ch03-02-data-types.html などを読みつつ試行錯誤を行う.

とりあえず 10 前後の整数を表現可能な最小の型 u8 を嵌めてみる:

let x; -> let x: u8;

これで rustlings watch をやり直すと,新しいエラー:

✅ Successfully ran exercises/variables/variables1.rs!
⚠️  Compiling of exercises/variables/variables2.rs failed! Please try again. Here's the output:
error[E0381]: use of possibly-uninitialized variable: `x`
 --> exercises/variables/variables2.rs:8:8
  |
8 |     if x == 10 {
  |        ^ use of possibly-uninitialized `x`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0381`.

Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

未初期化変数を参照するのは駄目,それはそう.では,後方に適当な代入文を付加したらどうなるか.

とりあえず代入文 x = 10; を宣言文直後に差し込んでみて,再挑戦すると呆気なく通過:

✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!

🎉 🎉  The code is compiling! 🎉 🎉

Output:
====================
Ten!

====================

You can keep working on this exercise,
or jump into the next one by removing the `I AM NOT DONE` comment:

 2 |  // Make me compile! Execute the command `rustlings hint variables2` if you want a hint :)
 3 |
 4 |  // I AM NOT DONE
 5 |
 6 |  fn main() {
Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

x == 10 以外の場合が気になり過ぎるので念の為見ておく:

✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!

🎉 🎉  The code is compiling! 🎉 🎉

Output:
====================
Not ten!

====================

You can keep working on this exercise,
or jump into the next one by removing the `I AM NOT DONE` comment:

 2 |  // Make me compile! Execute the command `rustlings hint variables2` if you want a hint :)
 3 |
 4 |  // I AM NOT DONE
 5 |
 6 |  fn main() {
Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

特に問題はなさそう.次に行く.

Debugging variables3.rs

✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!
⚠️  Compiling of exercises/variables/variables3.rs failed! Please try again. Here's the output:
error[E0384]: cannot assign twice to immutable variable `x`
 --> exercises/variables/variables3.rs:9:5
  |
7 |     let x = 3;
  |         -
  |         |
  |         first assignment to `x`
  |         help: make this binding mutable: `mut x`
8 |     println!("Number {}", x);
9 |     x = 5; // don't change this line
  |     ^^^^^ cannot assign twice to immutable variable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0384`.

Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

これは,これまでの知識だけで最小限のコストで太刀打ちできる.

mut キーワードを付けたら通った:

✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!
✅ Successfully ran exercises/variables/variables3.rs!

🎉 🎉  The code is compiling! 🎉 🎉

Output:
====================
Number 3
Number 5

====================

You can keep working on this exercise,
or jump into the next one by removing the `I AM NOT DONE` comment:

 2 |  // Make me compile! Execute the command `rustlings hint variables3` if you want a hint :)
 3 |
 4 |  // I AM NOT DONE
 5 |
 6 |  fn main() {
Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

Debugging variables4.rs

見たことのあるパターン,未初期化で怒られている:

✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!
✅ Successfully ran exercises/variables/variables3.rs!
⚠️  Compiling of exercises/variables/variables4.rs failed! Please try again. Here's the output:
error[E0381]: borrow of possibly-uninitialized variable: `x`
 --> exercises/variables/variables4.rs:8:27
  |
8 |     println!("Number {}", x);
  |                           ^ use of possibly-uninitialized `x`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0381`.

Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

今度は型注釈だけがあって代入がされていないパターン,さっき通過した中途半端なケースだ.

こういうときは,

  • 宣言時の型注釈だけやって代入文だけ別に払い出す
  • 型注釈するかしないかに関わらず宣言時に代入

のパターンがあると思うけど,綺麗に書きたい書き方を選ぶ余地があるのは良い.

とりあえず注釈を無くして適当な値を代入したら呆気なく成功:

✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!
✅ Successfully ran exercises/variables/variables3.rs!
✅ Successfully ran exercises/variables/variables4.rs!

🎉 🎉  The code is compiling! 🎉 🎉

Output:
====================
Number 5

====================

You can keep working on this exercise,
or jump into the next one by removing the `I AM NOT DONE` comment:

 2 |  // Make me compile! Execute the command `rustlings hint variables4` if you want a hint :)
 3 |
 4 |  // I AM NOT DONE
 5 |
 6 |  fn main() {
Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

Debugging variables5.rs


✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!
✅ Successfully ran exercises/variables/variables3.rs!
✅ Successfully ran exercises/variables/variables4.rs!
⚠️  Compiling of exercises/variables/variables5.rs failed! Please try again. Here's the output:
error[E0308]: mismatched types
 --> exercises/variables/variables5.rs:9:14
  |
9 |     number = 3;
  |              ^ expected `&str`, found integer

error[E0369]: cannot add `{integer}` to `&str`
  --> exercises/variables/variables5.rs:10:48
   |
10 |     println!("Number plus two is : {}", number + 2);
   |                                         ------ ^ - {integer}
   |                                         |
   |                                         &str

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0308, E0369.
For more information about an error, try `rustc --explain E0308`.

Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

見た感じ,型変換に関するエラーの模様.

// variables5.rs
// Make me compile! Execute the command `rustlings hint variables5` if you want a hint :)

// I AM NOT DONE

fn main() {
    let number = "T-H-R-E-E"; // don't change this line
    println!("Spell a Number : {}", number);
    number = 3;
    println!("Number plus two is : {}", number + 2);
}

創意工夫の余地がありそうだけど,ここは number という変数を使い回す方針で行きたい.

というわけで,以下の変更を施す:

  • 9 行目: number = 3; -> number = "3";
  • 10 行目: println!("Number plus two is : {}", (number as u8) + 2);

一瞬できないかなと思ったものの,プリミティブ型同士の casting は一応サポートされているそう:

https://doc.rust-lang.org/rust-by-example/types/cast.html

ちなみに,クォーテーションの規則は C と同じで

  • "<doubly-quoted>": 文字列
  • '<singly-quoted>': 文字

だそう,分かりやすくて良いね.

と思いきや,このアプローチも駄目:

✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!
✅ Successfully ran exercises/variables/variables3.rs!
✅ Successfully ran exercises/variables/variables4.rs!
⚠️  Compiling of exercises/variables/variables5.rs failed! Please try again. Here's the output:
error[E0606]: casting `&str` as `u8` is invalid
  --> exercises/variables/variables5.rs:10:41
   |
10 |     println!("Number plus two is : {}", (number as u8) + 2);
   |                                         ^^^^^^^^^^^^^^
   |
   = help: cast through a raw pointer first

error: aborting due to previous error

For more information about this error, try `rustc --explain E0606`.

Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

どうやら,整数型同士の変換以外はサポートされていないと思ったほうが良さげ.おとなしく別の変数を確保.

  • 9 行目: number = "3"; -> let number_i = 3;
  • 10 行目: println!("Number plus two is : {}", number_i + 2);

これで通過した.ちなみに,変数名の緊急回避をスネークケースで行ったが,スタイルガイドにも「局所変数はスネークケース」以外のことは書かれていない:

https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/advice.md

まあいいや.

Debugging variables6.rs

✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!
✅ Successfully ran exercises/variables/variables3.rs!
✅ Successfully ran exercises/variables/variables4.rs!
✅ Successfully ran exercises/variables/variables5.rs!
⚠️  Compiling of exercises/variables/variables6.rs failed! Please try again. Here's the output:
error: missing type for `const` item
 --> exercises/variables/variables6.rs:6:7
  |
6 | const NUMBER = 3;
  |       ^^^^^^ help: provide a type for the item: `NUMBER: i32`

error: aborting due to previous error


Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

型推論が効きそうなケースで型注釈を要求されている.きっと const キーワードで定数宣言がされているからに違いない.

https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html

を見ると,実際に型注釈が必要って書いてある.

でも型注釈が具体的に i32 で提案されているのは何故だろう...?

敢えて逆らって const NUMBER: i8 = 3; としてみても問題なかった:

✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!
✅ Successfully ran exercises/variables/variables3.rs!
✅ Successfully ran exercises/variables/variables4.rs!
✅ Successfully ran exercises/variables/variables5.rs!
✅ Successfully ran exercises/variables/variables6.rs!

🎉 🎉  The code is compiling! 🎉 🎉

Output:
====================
Number 3

====================

You can keep working on this exercise,
or jump into the next one by removing the `I AM NOT DONE` comment:

 2 |  // Make me compile! Execute the command `rustlings hint variables6` if you want a hint :)
 3 |
 4 |  // I AM NOT DONE
 5 |
 6 |  const NUMBER: i8 = 3;
Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

let の場合もそうだけど,基本的に型注釈は左辺値で行う(当たり前):

let a: u8 = 4;
let b: i32 = -10;

というわけで,variables 編は無事に終わり.

プリミティブ型の種類に深入りすることなく終わったけど,本来プログラミング言語を新しく学ぶときってこのくらいのことだけ抑えておけば十分である.

というわけで,次回は functions 編.

次回予告


✅ Successfully ran exercises/variables/variables1.rs!
✅ Successfully ran exercises/variables/variables2.rs!
✅ Successfully ran exercises/variables/variables3.rs!
✅ Successfully ran exercises/variables/variables4.rs!
✅ Successfully ran exercises/variables/variables5.rs!
✅ Successfully ran exercises/variables/variables6.rs!
⚠️  Compiling of exercises/functions/functions1.rs failed! Please try again. Here's the output:
error[E0425]: cannot find function `call_me` in this scope
 --> exercises/functions/functions1.rs:7:5
  |
7 |     call_me();
  |     ^^^^^^^ not found in this scope

error: aborting due to previous error

For more information about this error, try `rustc --explain E0425`.

Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

スコープという概念が登場.

Discussion