4
1

前の記事

全記事一覧

100 Exercise To Learn Rust 第2回改め演習第1回になります!今回からは前置きはほどほどに問題に取り組んでいきます!

今回の関連ページ

[01_intro/01_syntax] 基本的な文法

問題はこちらです。

lib.rs
// 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においてコンパイラは最高の友達だからちゃんとコンパイラ君の助言に耳を傾けよう!」 といった旨のことが書かれています(超意訳)。実際そうなので今回はまずコンパイルエラーを見てみます。

100-exercises-to-learn-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は静的型付け言語なので特に関数のシグネチャでは型を明記する必要があります。

解説

問題箇所に戻ります。ab は小文字で始まっている某と見ることができます。Rustは、変数名はスネークケース snake_case 、型名やトレイト名はパスカルケース PascalCase慣例的に決まっているので、これらは変数名を意図したものでしょう。

というわけで、型名を添えてあげます。Rustは当然暗黙キャストをしないので、最小の変更にするには(もとい Don't touch the function body. を守るには) u32 型にするのが良さそうでしょう。

lib.rs
- 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

解説

4u8u8 型の 4 であることを表すリテラルです。暗黙の型変換は行われないため、もとより u32 型の 4 であったことにしておけば具合が良さそうです。

lib.rs
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
}

u32u8uunsigned integer の ui32i8iinteger の i です。Rustは、バグを防ぐため負数を表現する必要がないときはだいたい非負整数にしていることが多い言語だと思います。

2の補数でのバイト幅だ等色々書かれていますが「結局最大最小値は?」ってなりますよね。これらの値は MINMAX メソッドで確認することが可能です。

また本節では登場していませんが、 usize が一番目にすることが多い整数型になると思います。というのもこの整数型だけは特殊で、配列の添字に使用することが可能なためです。要はアドレス値に近い部分で使われる型というわけで、32bit OSなら32bit、64bit OSなら64bit幅を持つために size という語が割り当てられています。

[02_basic_calculator/02_variables] 変数

問題はこちらです(長いため序盤のコメントは省略)。

lib.rs
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);
    }
}
コメントあり
lib.rs
// 👇 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 変数として束縛してあげると良さそうです。

lib.rs
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は「 推論できる場合は型をグレーで表示してくれる 」機能を持ちます。

image.png

「Rustとっつきにくかったけど、rust-analyzerが型注釈してくれるようになってから使いやすくなった」とは友人談ですが、実際この機能がRustを書きやすくかつ型に対して厳格であるという現状を実現しており、VSCode+rust-analyzerを最高のRustエディタたらしめています。


最後の問題が特徴的でしたが、この3問より

  • 関数のシグネチャは人間が確実に指定しなければならない
  • 暗黙の型キャストなんて当然許さない強い静的型付け言語である
  • 型推論により変数の型注釈はある程度省略できる

ことがわかりました。常に型注釈しなければならないわけではなく、一方で関数のシグネチャという必要な箇所では人間が型注釈しなければいけないという特徴より、Rustの型は他言語と比べてもとっつきやすいものになっているんじゃないかと筆者的には思います。

では次の問題に行きましょう!

次の記事: 【2】 if・パニック・演習

  1. 100 Exercisesの方でも言及されていますが、例外として、スマートポインタ(もといDerefトレイト実装型)や参照型関連については暗黙変換されることがあります。といっても基本的に *& が頭についている時の話ですから、結局どの箇所でも変換がほぼ明示されている感じになっています。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1