ちゃなべの備忘録

ほぼ備忘録です。

Rustを触ってみた②【備忘録】

ayumu1212.hatenablog.com

こちらの続きです。

3. Common Programming Concepts

この章は他のプログラミング言語にもあるような概念が、Rustではどのように表現されているかを示すらしい。慣例も話してくれるし、変数、型、関数、コメント、制御フローなどを学びます。

3.1. Variables and Mutability

Variables (変数)

デフォルトでは変数は不変です。理由はこのルールの方がバグの発生をより防ぐことができるからです。

もちろんmutといれたら可変になるよ。変数の宣言はletと言ってね。

fn main() {
    let mut x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}
Constants (定数)

定数?変数のimmutableと何が違うん?

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

スコープの縛りはないから、ハードコードしないといけない値に名前をつけてあげて。

Shadowing

同じ名前で変数を再宣言するやつ。再代入には厳しいけど、再宣言はいいよって感じなのかな?

fn main() {
    let x = 5;

    let x = x + 1;

    {
        let x = x * 2;
        println!("The value of x in the inner scope is: {x}"); // 12
    }

    println!("The value of x is: {x}"); // 6
}

また、再宣言する時に前の型を踏襲しなくてもいい。これはmutでやろうとするとエラーが出るやつ。

    let spaces = "   ";
    let spaces = spaces.len();

3.2. Data Types

Rustは静的型付け言語なので、コンパイル時に全ての変数の型がわかっていなければなりません。値と使い方から推測することができます。

Scalar Types (スカラー)

4つあります。

  1. Integer Types

  2. 小数部分を含まない数値。

  3. iが符号を含み、uが符号なしです。
  4. bit数によって決まります。
  5. 例) i8, u16, i32, u64, i128, usize
  6. sizeはプログラムが実行されているコンピューターの型に依存します。

値自体は
アンスコを入れてわかりやすくできるし(12_000_000)
値の末尾に型を指定できるし(53u16)
逆に先頭で指定もできる(0xff)

オーバーフローした時は、developモードではpanicになり、releaseモードの時は0に回り込みます。オーバーフローしたかどうかのメソッドがいくつか提供されているので、必要であればそれを使ってください。

演算子は普通に使えます。

  1. Floating-Point Types

f32とf64の2種類。デフォルトでf64です。全部符号ありです。

fn main() {
    let x = 2.0; // f64

    let y: f32 = 3.0; // f32
}
  1. Boolean Type

boolです。

fn main() {
    let t = true;

    let f: bool = false; // with explicit type annotation
}
  1. Character Type

これは文字列リテラルじゃなくて、charリテラルです。

  • 文字列の場合はダブルクオーテーション、charの場合はシングルクオーテーション
  • 4バイトあり、Unicodeスカラー値を表します、絵文字とかもいけるよ。
fn main() {
    let c = 'z';
    let z: char = 'ℤ'; // with explicit type annotation
    let heart_eyed_cat = '😻';
}
Compound Types (複合型)

複数の値を一つの型にまとめられる。タプルと配列という2つのプリミティブな複合型がRustにはある。

  1. Tuple Type

タプル型

  • 固定された長さ、宣言後にサイズが変化しない
  • 各位置の型は異なっていいです
  • インデックスが決まり、それぞれドットで繋げてアクセスできる
fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (x, y, z) = tup;

    println!("The value of tup.0 is: {0}", tup.0); // 500
    println!("The value of y is: {y}") // 6.4
}
  1. Array Type

配列型

  • 固定長
  • 各要素の型が同じでないとだめ
  • 型の定義の仕方ちょっと特殊
fn main() {
    let a: [i32; 5] = [1, 2, 3, 4, 5];
}

ベクター型という標準ライブラリで提供されている型があるのだが、そっちはサイズを変化させられる。配列かベクターか迷ったら、ベクター使った方がいいらしい。もし固定長なら配列。

初期値の定義がちょっとおもろくできるよ。あとアクセスはindexを指定してね。

fn main() {
    let a = [3; 5]; // 3の初期値で長さが5

    let second = a[1];
}

もし存在しないindexの値を指定したら、すぐにpanicを起こして不正なインデックスにアクセスさせることを防ぎます。

3.3. Functions

Parameters

関数です。スネークケースで書いてください。ちゃんとスコープを考えて定義されていたら、関数はどこに定義していたとしても構いません。

パラメータも想像に難くない書き方をします。

fn main() {
    print_labeled_measurement(3, 'h');
}

fn print_labeled_measurement(value: i32, unit_label: char) {
    println!("The measurement is: {value}{unit_label}")
}
Statements and Expressions

ステートメントと式の違いはわかった方がいい。

  • Statements(ステートメント): 何らかのアクションがあるけど、値は返さない命令
  • Expressions(式): 何らかのアクションを行い、値も返す「ことができる」命令

ぶっちゃけ式の説明はあっているかわからない、けどそうっぽいんだもん。返さないこともできるからね。

let y = 6; みたいな書き方はこれはステートメント、戻り値をかえしません。そしてステートメントは末尾にセミコロンをつけます。

逆に以下のような書き方があった場合、

fn main() {
    let y = {
        let x = 3;
        x + 1
    };

    println!("The value of y is: {y}");
}

このyに代入しているこれ。

{
    let x = 3;
    x + 1
}

これは最後の x + 1は式です。これが値を返して、yに代入します。そして式にはセミコロンをつけません。つけたらステートメントになってしまい、値を返さないようになってしまいます。

ちなみに、式→ステートメントの順番で書いたら怒られた。その順番で書くのは基本的にダメっぽいな。セミコロンが最後はいらないのは、そういうことか。ずっとセミコロンを「改行」っていう意味でしか考えてなかったから、「最後は改行いらないからセミコロンなくていいんだろうな〜〜」くらいにしか思ってなかった。

Functions with Return Values

戻り値設定しようね。基本的には最後の式から返される。

fn five() -> i32 {
    5
}

fn main() {
    let x = five();

    println!("The value of x is: {x}")
}

↑の例だと、five() 関数の5にセミコロンをつけたらコンパイルエラーになります、戻り値をi32と言っているのに、何も返さないようになっちゃうので。

3.4. Comments

こうやってね、終わり。なんか複数行に渡るコメントの仕方がないんだな。

// So we’re doing something complicated here, long enough that we need
// multiple lines of comments to do it! Whew! Hopefully, this comment will
// explain what’s going on.
fn main() {
    let lucky_number = 7; // I’m feeling lucky today
}

3.5. Control Flow

ifとループに関するお話。

if Expressions

普通の書き方をします。ifの直後の条件のようなものは arm と呼ばれたりもする。

fn main() {
    let number = 3;

    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }
}

なお、if文の条件は必ずboolじゃないとダメです。「 1はtrueでしょww」とかいう甘えは許されません。

Handling Multiple Conditions with else if

else ifもあります。けど煩雑になりがちで、matchというものを使った方がいいらしい。それは6章で。

fn main() {
    let number = 3;

    if number < 5 {
        println!("low number: {number}");
    } else if number < 15 {
        println!("mideum number: {number}");
    } else {
        println!("condition was false");
    }
}
Using if in a let Statement

ifは式だから、letの右にかけるらしいぜ。

fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 };

    println!("The value of number is: {number}");
}

これ、5と6の右にセミコロンつけて、何も返さないようにしたらどうなるんだろ、って思ってやったら一応行けた。だけどwarningはでた。

これ、ifの条件によって型を変えることできるんかな?って思ったらできなかった。i32とi64とかでもダメだった。

つまり、実行時に型が決まるような変数はRustにおいてはできないってこと。

TSとかだったら、リテラル型とかできるけど、それもできなそうだしなぁ。強いていうならタプルって感じか。

Repeating Code with loop

loopは永遠に繰り返す。breakとかで抜けようね。

fn main() {
    loop {
        println!("again!");
    }
}

うわ、if も loopもセミコロンつけてもつけなくてもコンパイルエラー起こらん。。式だからか。。

Returning Values from Loops

loopは式だから、letの横に置けるらしい。

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("The result is {result}");
}

そして、なんじゃ、そのbreak...。見たことないぞ、、だけど直感的にわかりやすくて最高だぞ。

そして、breakの代わりにreturnを使ってみようと思ったけど怒られた。return って関数の戻り値だから、いまのloop で使うとmainを抜けちゃうわ。

Loop Labels to Disambiguate Between Multiple Loops

loopは重ねられる。そして、breakとcontinueはすぐ外のloopに対して適用されます。けど、labelというものをつけるだけで、どのloopから抜けるか決めることができるっちょ。

fn main() {
    let mut count = 0;
    'counting_up: loop {
        println!("count = {count}");
        let mut remaining = 10;

        loop {
            println!("remaining = {remaining}");
            if remaining == 9 {
                break;
            }
            if count == 2 {
                break 'counting_up;
            }
            remaining -= 1;
        }

        count += 1;
    }
    println!("End count = {count}");
}

えぇ。。。書き方キモォ。。 シングルクオートを閉じないだと??すげぇな。めっちゃTypoしそう。

Conditional Loops with while

whileはこう書くよ。

fn main() {
    let mut number = 3;

    while number != 0 {
        println!("{number}!");

        number -= 1;
    }

    println!("LIFTOFF!!!");
}
Looping Through a Collection with for

forはこう書くよ。

fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a {
        println!("the value is: {element}");
    }
}

配列に対してはこれを使った方がいいね。