🦀

Rustの勉強 3日目

2022/09/29に公開

The Rust Programming Language 日本語版 第3章

3.1 変数と可変性

第2章のハンズオンの中で小出しに言語機能について紹介されていたけれど、あらためて整理して基本的な言語機能をここで紹介するらしい。

  • 変数はデフォルトでイミュータブル。ミュータブルにしたかったら mut キーワードを付ける。
  • 変数は let で宣言、定数は const で宣言。定数の場合は型注釈を付ける必要がある。
    • 定数はスコープ内でのみ有効
    • 定数はアンダースコア区切りで大文字のみで命名
  • シャドーイングにより同じ変数名で変数を覆うことが可能
    • シャドーイングはスコープ内でのみ有効なので、スコープを抜けたら前の変数が戻る
    • シャドーイングは型が変わっても良い

3.2 データ型

  • 変数宣言のときに複数の型が可能な場合には必ず型注釈を与えなければいけない
  • 単独の値を持つスカラー型には、整数、浮動小数点数、真偽値、文字がある
  • 複数の値を一つにまとめた複合型はタプル型と配列型がある。
    • タプルはPythonと同じように丸カッコとカンマで表記する。(例: let tup: (i32, f64, u8) = (500, 6.4, 1);
    • 要素にはピリオドを使って添字でアクセス可能。(例: let x = (1, 2); x.0
    • 配列は全要素同じ型でなければならず、長さは固定長。可変長のものが欲しかったら標準ライブラリのベクタ型を使う。
    • 配列型の型宣言は次のように行う。 let a: [i32; 5] = [1, 2, 3, 4, 5];
    • 要素にはカギカッコと添字でアクセスする。(例: let x = [1, 2, 3]; x[0]
    • index out of bounds ではパニックになる。これも他の言語と同じなのであまり驚きはない。

3.3 関数

  • Rustの関数と変数の命名規則はスネークケース
  • fn キーワードで宣言
  • 定義順は関係ない(多くのLLとは異なる)
  • 関数シグネチャでは必ず仮引数の型と戻り値の型を宣言しなければならない
  • 戻り値は return キーワードで返すこともできるが、通常は暗黙的に最後の式が戻り値となる
    • このとき文と式の違いをよく気をつける必要がある(末尾にセミコロンがあると文になってしまう)
fn function_name(var1: type1, var2: type2) -> ret_type {
    let x = 5; // これは文
    x + 1 // これは式
}

3.4 コメント

Rustはコメントは // で行コメントを書く。複数行でも各行の先頭に // を書く。

3.5 制御フロー

  • if 式はカッコを付けずに条件式を書く(Goと同じ感じ)
    • else if で複数条件の追加
  • let 文内で if 式を使うことで三項演算的なことが出来る( let x = if condition { 5 } else { 6 };
  • 繰り返しにはいくつかのキーワードがある
    • loop は条件なしに強制的にループを繰り返す場合に使う。
    • ループラベルというものを使うとネストされた loop から break で脱出するときに上のループに脱出できる
    • while を使うと他の言語と同様の条件付き繰り返しができる
    • for .. in 構文でコレクションの要素を繰り返せる(Pythonに似てる)

Rustlings

ここで、3章を読んだのでRustlingsを演習としてやってみる。

variables1.rs

宣言してない変数 x に値を代入しているので let キーワードを追加する。

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

variables2.rs

if式で x に値が入っていないのでおかしなことになるので、 10 を代入する。

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

variables3.rs

代入部分がなかったので、追加した。

fn main() {
    let x: i32 = 2;
    println!("Number {}", x);
}

variables4.rs

一度宣言された変数の値を上書きしようとしているので、 x はミュータブルにしなければいけないので mut キーワードを追加。

fn main() {
    let mut x = 3;
    println!("Number {}", x);
    x = 5; // don't change this line
    println!("Number {}", x);
}

variables5.rs

既存の変数の値を上書きしようとしているが、値の型が違うのでシャドーイングで解決した。

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

variable6.rs

const は型注釈が必要なので追加。

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

functions1.rs

未定義の関数 call_me を呼んでいたので、関数シグネチャが合うように宣言した。

fn main() {
    call_me();
}

fn call_me() {
    println!("hello, function")
}

functions2.rs

仮引数に型注釈がなかったので追加した。

fn main() {
    call_me(3);
}

fn call_me(num: i32) {
    for i in 0..num {
        println!("Ring! Call number {}", i + 1);
    }
}

functions3.rs

call_meu32 の引数を取るように宣言されているのに、引数なしで呼ばれてるので、適当に u32 の値を渡して呼ぶ。

fn main() {
    call_me(1);
}

fn call_me(num: u32) {
    for i in 0..num {
        println!("Ring! Call number {}", i + 1);
    }
}

functions4.rs

sale_price の関数シグネチャで戻り値の型注釈がなかったので追加。

fn main() {
    let original_price = 51;
    println!("Your sale price is {}", sale_price(original_price));
}

fn sale_price(price: i32) -> i32 {
    if is_even(price) {
        price - 10
    } else {
        price - 3
    }
}

fn is_even(num: i32) -> bool {
    num % 2 == 0
}

functions5.rs

square の最後の行にセミコロンがあって式ではなく文になっていたのでセミコロンを削除した。

fn main() {
    let answer = square(3);
    println!("The square of 3 is {}", answer);
}

fn square(num: i32) -> i32 {
    num * num
}

if1.rs

ただ if 式を書いただけ

pub fn bigger(a: i32, b: i32) -> i32 {
    // Complete this function to return the bigger number!
    // Do not use:
    // - another function call
    // - additional variables
    if a > b {
        a
    } else {
        b
    }
}

if2.rs

foo_if_fizz の戻り値が &str だったのに、最初は else1 を返していたので、文字列を返すようにしなければいけないというのはわかっていた。同時に、テストケースが解決するように else if で条件を追加した。

pub fn foo_if_fizz(fizzish: &str) -> &str {
    if fizzish == "fizz" {
        "foo"
    } else if fizzish == "fuzz" {
        "bar"
    } else {
        "baz"
    }
}

primitive_types1.rs

is_evening が定義されていなかったので let 文を完成させた。

fn main() {
    // Booleans (`bool`)

    let is_morning = true;
    if is_morning {
        println!("Good morning!");
    }

    let is_evening = false; // Finish the rest of this line like the example! Or make it be false!
    if is_evening {
        println!("Good evening!");
    }
}

primitive_types2.rs

まだ文字型についてはドキュメントに出てきてなかったが、例のコメントにシングルクォートだと文字型になるよと書いてあって、 your_character が定義されていなかったので適当に値を与えた。

fn main() {
    // Characters (`char`)

    // Note the _single_ quotes, these are different from the double quotes
    // you've been seeing around.
    let my_first_initial = 'C';
    if my_first_initial.is_alphabetic() {
        println!("Alphabetical!");
    } else if my_first_initial.is_numeric() {
        println!("Numerical!");
    } else {
        println!("Neither alphabetic nor numeric!");
    }

    let your_character = '@'; // Finish this line like the example! What's your favorite character?
                              // Try a letter, try a number, try a special character, try a character
                              // from a different language than your own, try an emoji!
    if your_character.is_alphabetic() {
        println!("Alphabetical!");
    } else if your_character.is_numeric() {
        println!("Numerical!");
    } else {
        println!("Neither alphabetic nor numeric!");
    }
}

とりあえず、このあたりまでがThe Rust Programming Languageの第3章がカバーしている範囲っぽいのでここで終了。続きはまた明日。

Discussion