前の記事
- 【0】 準備
- 【1】 構文・整数・変数 ← 今回
全記事一覧
- 【0】 準備
- 【1】 構文・整数・変数
- 【2】 if・パニック・演習
- 【3】 可変・ループ・オーバーフロー
- 【4】 キャスト・構造体 (たまにUFCS)
- 【5】 バリデーション・モジュールの公開範囲 ~ → カプセル化!~
- 【6】 カプセル化の続きと所有権とセッター ~そして不変参照と可変参照!~
- 【7】 スタック・ヒープと参照のサイズ ~メモリの話~
- 【8】 デストラクタ(変数の終わり)・トレイト ~終わりと始まり~
- 【9】 Orphan rule (孤児ルール)・演算子オーバーロード・derive ~Empowerment 💪 ~
- 【10】 トレイト境界・文字列・Derefトレイト ~トレイトのアレコレ~
- 【11】 Sized トレイト・From トレイト・関連型 ~おもしろトレイトと関連型~
- 【12】 Clone・Copy・Dropトレイト ~覚えるべき主要トレイトたち~
- 【13】 トレイトまとめ・列挙型・match式 ~最強のトレイトの次は、最強の列挙型~
- 【14】 フィールド付き列挙型とOption型 ~チョクワガタ~
- 【15】 Result型 ~Rust流エラーハンドリング術~
- 【16】 Errorトレイトと外部クレート ~依存はCargo.tomlに全部お任せ!~
- 【17】 thiserror・TryFrom ~トレイトもResultも自由自在!~
- 【18】 Errorのネスト・慣例的な書き方 ~Rustらしさの目醒め~
- 【19】 配列・動的配列 ~スタックが使われる配列と、ヒープに保存できる動的配列~
- 【20】 動的配列のリサイズ・イテレータ ~またまたトレイト登場!~
- 【21】 イテレータ・ライフタイム ~ライフタイム注釈ようやく登場!~
- 【22】 コンビネータ・RPIT ~ 「
Iterator
トレイトを実装してるやつ」~ - 【23】
impl Trait
・スライス ~配列の欠片~ - 【24】 可変スライス・下書き構造体 ~構造体で状態表現~
- 【25】 インデックス・可変インデックス ~インデックスもトレイト!~
- 【26】 HashMap・順序・BTreeMap ~Rustの辞書型~
- 【27】 スレッド・'staticライフタイム ~並列処理に見るRustの恩恵~
- 【28】 リーク・スコープ付きスレッド ~ライフタイムに技あり!~
- 【29】 チャネル・参照の内部可変性 ~Rustの虎の子、mpscと
Rc<RefCell<T>>
~ - 【30】 双方向通信・リファクタリング ~返信用封筒を入れよう!~
- 【31】 上限付きチャネル・PATCH機能 ~パンクしないように制御!~
- 【32】
Send
・排他的ロック(Mutex
)・非対称排他的ロック(RwLock
) ~真打Arc<Mutex<T>>
登場~ - 【33】 チャネルなしで実装・Syncの話 ~考察回です~
- 【34】
async fn
・非同期タスク生成 ~Rustの非同期入門~ - 【35】 非同期ランタイム・Futureトレイト ~非同期のお作法~
- 【36】 ブロッキング・非同期用の実装・キャンセル ~ラストスパート!~
- 【37】 Axumでクラサバ! ~最終回~
- 【おまけ1】 Rustで勘違いしていたこと3選 🏄🌴 【100 Exercises To Learn Rust 🦀 完走記事 🏃】
- 【おまけ2】 【🙇 懺悔 🙇】Qiitanグッズ欲しさに1日に33記事投稿した話またはQiita CLIとcargo scriptを布教する的な何か
100 Exercise To Learn Rust 第2回改め演習第1回になります!今回からは前置きはほどほどに問題に取り組んでいきます!
今回の関連ページ
[01_intro/01_syntax] 基本的な文法
問題はこちらです。
// TODO: fix the function signature below to make the tests pass.
// Make sure to read the compiler error message—the Rust compiler is your pair programming
// partner in this course and it'll often guide you in the right direction!
//
// The input parameters should have the same type of the return type.
fn compute(a, b) -> u32 {
// Don't touch the function body.
a + b * 2
}
#[cfg(test)]
mod tests {
use crate::compute;
#[test]
fn case() {
assert_eq!(compute(1, 2), 5);
}
}
「Rustにおいてコンパイラは最高の友達だからちゃんとコンパイラ君の助言に耳を傾けよう!」 といった旨のことが書かれています(超意訳)。実際そうなので今回はまずコンパイルエラーを見てみます。
$ wr
Running tests...
✅ (01) intro - (00) welcome (Skipped)
❌ (01) intro - (01) syntax
Meditate on your approach and return. Mountains are merely mountains.
error: expected one of `:`, `@`, or `|`, found `,`
--> exercises/01_intro/01_syntax/src/lib.rs:6:13
|
6 | fn compute(a, b) -> u32 {
| ^ expected one of `:`, `@`, or `|`
|
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
help: if this is a `self` type, give it a parameter name
|
6 | fn compute(self: a, b) -> u32 {
| +++++
help: if this is a parameter name, give it a type
|
6 | fn compute(a: TypeName, b) -> u32 {
| ++++++++++
help: if this is a type, explicitly ignore the parameter name
|
6 | fn compute(_: a, b) -> u32 {
| ++
error: expected one of `:`, `@`, or `|`, found `)`
--> exercises/01_intro/01_syntax/src/lib.rs:6:16
|
6 | fn compute(a, b) -> u32 {
| ^ expected one of `:`, `@`, or `|`
|
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
help: if this is a parameter name, give it a type
|
6 | fn compute(a, b: TypeName) -> u32 {
| ++++++++++
help: if this is a type, explicitly ignore the parameter name
|
6 | fn compute(a, _: b) -> u32 {
| ++
error: could not compile `syntax` (lib) due to 2 previous errors
error: could not compile `syntax` (lib test) due to 2 previous errors
慣れないうちは長いエラーに圧倒されるかもしれませんが、解決法に直結しない投げやりなエラーではなく、大体は調べやすいエラーを返してくれるので素直に読んでみると学びが多いです。
エラーは2つ表示されていますがどちらもほぼ同じものです。
error: expected one of `:`, `@`, or `|`, found `,`
--> exercises/01_intro/01_syntax/src/lib.rs:6:13
|
6 | fn compute(a, b) -> u32 {
| ^ expected one of `:`, `@`, or `|`
|
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685)
文法の問題なので文法についてエラーになっていますね。当たり前体操
その下に3つほどhelpが出ていますが、これらはこちらの意図をいくつか汲み取り分類しているもののようです。
-
self
型ならば、self
を付けてください-
self
はメソッドを定義する際の糖衣構文なので今回は当てはまらないです
-
- パラメータ (引数名) を意図したものならば、型を書いてください
- 型名を意図したものならば、引数名を付けてください
- (とは書いておらず、
_
を利用して使わない引数ということにしてくださいと書いていますね)
- (とは書いておらず、
今回は2番目か3番目、つまり、「引数名と型」の両方を明示しなければならないのに、片方しか書いていない という旨のエラーと捉えると良さそうです。
変数への束縛時は型名を省略できたりするのですが(後述。静的に決まる必要あり)、Rustは静的型付け言語なので特に関数のシグネチャでは型を明記する必要があります。
解説
問題箇所に戻ります。a
や b
は小文字で始まっている某と見ることができます。Rustは、変数名はスネークケース snake_case
、型名やトレイト名はパスカルケース PascalCase
と慣例的に決まっているので、これらは変数名を意図したものでしょう。
というわけで、型名を添えてあげます。Rustは当然暗黙キャストをしないので、最小の変更にするには(もとい Don't touch the function body.
を守るには) u32
型にするのが良さそうでしょう。
- fn compute(a, b) -> u32 {
+ fn compute(a: u32, b: u32) -> u32 {
// Don't touch the function body.
a + b * 2
}
無事に通りました!次の問題にいきましょう。
(順当に行くと 02_basic_calculator/00_intro なのですが、どうやらタイトルコールを一緒に行うだけみたいなので飛ばします。以降の章も同様らしい)
[02_basic_calculator/01_integers] 整数型
問題はこちらです。
fn compute(a: u32, b: u32) -> u32 {
// TODO: change the line below to fix the compiler error and make the tests pass.
a + b * 4u8
}
#[cfg(test)]
mod tests {
use crate::compute;
#[test]
fn case() {
assert_eq!(compute(1, 2), 9);
}
}
さっきの問題と似ていますが 暗黙の型変換がない ことを強調しているようです1。
解説
4u8
は u8
型の 4
であることを表すリテラルです。暗黙の型変換は行われないため、もとより u32
型の 4
であったことにしておけば具合が良さそうです。
fn compute(a: u32, b: u32) -> u32 {
// TODO: change the line below to fix the compiler error and make the tests pass.
- a + b * 4u8
+ a + b * 4u32
}
u32
や u8
の u
は unsigned integer の u
、 i32
や i8
の i
は integer の i
です。Rustは、バグを防ぐため負数を表現する必要がないときはだいたい非負整数にしていることが多い言語だと思います。
2の補数でのバイト幅だ等色々書かれていますが「結局最大最小値は?」ってなりますよね。これらの値は MIN
や MAX
メソッドで確認することが可能です。
例
また本節では登場していませんが、 usize
が一番目にすることが多い整数型になると思います。というのもこの整数型だけは特殊で、配列の添字に使用することが可能なためです。要はアドレス値に近い部分で使われる型というわけで、32bit OSなら32bit、64bit OSなら64bit幅を持つために size
という語が割り当てられています。
[02_basic_calculator/02_variables] 変数
問題はこちらです(長いため序盤のコメントは省略)。
pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: define a variable named `distance` with the right value to get tests to pass
// Do you need to annotate the type of `distance`? Why or why not?
// Don't change the line below
distance / time_elapsed
}
#[cfg(test)]
mod tests {
use crate::speed;
#[test]
fn case1() {
assert_eq!(speed(0, 10, 10), 1);
}
#[test]
fn case2() {
assert_eq!(speed(10, 30, 10), 2);
}
#[test]
fn case3() {
assert_eq!(speed(10, 31, 10), 2);
}
}
コメントあり
// 👇 The lines below, starting with `///`, are called **documentation comments**.
// They attach documentation to the item that follows them. In this case, the `speed` function.
// If you run `cargo doc --open` from this exercise's directory, Rust will generate
// HTML documentation from these comments and open it in your browser.
/// Given the start and end points of a journey, and the time it took to complete it,
/// calculate the average speed.
pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: define a variable named `distance` with the right value to get tests to pass
// Do you need to annotate the type of `distance`? Why or why not?
// Don't change the line below
distance / time_elapsed
}
#[cfg(test)]
mod tests {
use crate::speed;
#[test]
fn case1() {
assert_eq!(speed(0, 10, 10), 1);
}
#[test]
fn case2() {
assert_eq!(speed(10, 30, 10), 2);
}
#[test]
fn case3() {
assert_eq!(speed(10, 31, 10), 2);
}
}
///
はドキュメントコメントであるといった旨が書かれていますね。知っとくと便利ではありますが問題とは関係ないです。
distance
という変数が定義されていないので、定義してくださいというものです。
解説
終点座標から始点座標を引けば距離になるので、それを distance
変数として束縛してあげると良さそうです。
pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: define a variable named `distance` with the right value to get tests to pass
// Do you need to annotate the type of `distance`? Why or why not?
+ let distance = end - start;
// Don't change the line below
distance / time_elapsed
}
エクササイズの意図としては、「強い静的型付け言語なのに distance
に型を書く( let distance: u32 = ...
とする)必要はないの?」という発問を促したかったみたいですね。Rustは結構強力な 型推論 機構を持っており、関係する演算等から型が推論できる場合エラーになりません。
なんならVSCodeのrust-analyzerは「 推論できる場合は型をグレーで表示してくれる 」機能を持ちます。
「Rustとっつきにくかったけど、rust-analyzerが型注釈してくれるようになってから使いやすくなった」とは友人談ですが、実際この機能がRustを書きやすくかつ型に対して厳格であるという現状を実現しており、VSCode+rust-analyzerを最高のRustエディタたらしめています。
最後の問題が特徴的でしたが、この3問より
- 関数のシグネチャは人間が確実に指定しなければならない
- 暗黙の型キャストなんて当然許さない強い静的型付け言語である
- 型推論により変数の型注釈はある程度省略できる
ことがわかりました。常に型注釈しなければならないわけではなく、一方で関数のシグネチャという必要な箇所では人間が型注釈しなければいけないという特徴より、Rustの型は他言語と比べてもとっつきやすいものになっているんじゃないかと筆者的には思います。
では次の問題に行きましょう!
次の記事: 【2】 if・パニック・演習