Open9

Rust学習

tigercat1124tigercat1124

cargo

new

cargo new xxxx

build

cargo build

target/debugに作成される

cargo build --release

target/releaseのディレクトリに作成される。

run

cargo run

check

cargo check

ビルドをしないコンパイルができるので、コンパイルエラーの確認に役に立つ

tigercat1124tigercat1124

数当てゲーム

code

let apples = 5; // immutable
                // 不変
let mut bananas = 5; // mutable
                     // 可変

rustは基本的に不変な値を作成する

constは定数的な値
letは関数など処理後に生成された値

let mutで初めてか変の値を取得することができる。

io::stdin()
    .read_line(&mut guess)

もし、use std::ioでライブラリをインポートしていなくても以下のように書ける

std::io::stdin().read_line

プレースホルダー

プレースホルダーに言い方が可愛い。

{}は値を所定の場所に保持する小さなカニのハサミだと考えてください。


#![allow(unused)]
fn main() {
let x = 5;
let y = 10;

println!("x = {} and y = {}", x, y);
}

クレード

依存関係の記載

乱数を作成するために、randクレートを使用する。
この時のクレートは、ライブラリクレートと言われる

Cargoがその力を発揮するの外部クレートと連携する時です。

Cargo.toml

rand = "0.8.3"

依存関係のアップデート

本当にアップグレートしたくなったときのために、updateコマンドを提供しています。

cargo update

乱数の作成

use std::io;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..101);

    println!("The secret number is: {}", secret_number);    //秘密の数字は次の通り: {}

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}

開始..終了という形式で、上限値は含みません。あるいは1..=100という範囲で渡すこともできます。

クレートのドキュメントの出力

cargo doc --openコマンドを走らせると、すべての依存クレートが提供するドキュメントをローカルでビルドして、ブラウザで開いてくれることです。

比較

match式は複数のアーム(腕)で構成されます。 各アームはマッチさせるパターンと、matchに与えられた値がそのアームのパターンにマッチしたときに実行されるコードで構成されます。

アーム?... パターンの意味はわかるが、そんな言い方をするのか。これも蟹とかけてるのかな

use std::cmp::Ordering;

func main(){
    match guess.cmp(&srecret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}

シャドーイング

parse処理に型を指定しないとエラーになった。
ジェネリクス的なものを見ている?parse関数を調べる必要がある。

文字列のparseメソッドは文字列をパース(解析)して何らかの数値にします。 このメソッドは(文字列を)さまざまな数値型へとパースできるので、let guess: u32としてRustに正確な数値型を伝える必要があります。 guessの後にコロン(:)を付けることで変数の型に注釈をつけることをRustに伝えています。

    let num = "-5";
    let num = num.trim().parse().expect("Please type a number!");
error[E0284]: type annotations needed
  --> src/main.rs:14:9
   |
14 |     let num = num.trim().parse().expect("Please type a number!");
   |         ^^^              ----- type must be known at this point

loop

無限ループを作れる

  • ctrl + cで抜ける
  • quitなどの特定の文字列で抜ける
  • breakで抜ける
tigercat1124tigercat1124

可変と不変

シャドーイングできちゃうから、mutじゃなくても書けそう。

    let num = "-5";
    println!("num is {}",num);
    let num = 5;
    println!("num is {}",num);

ちゃんと言及されていた。

シャドーイングは、変数をmutにするのとは違います。なぜなら、letキーワードを使わずに、 誤ってこの変数に再代入を試みようものなら、コンパイルエラーが出るからです。letを使うことで、 値にちょっとした加工は行えますが、その加工が終わったら、変数は不変になるわけです。

不変ではあるけど、let使いまくったらじゃないじゃんとも思った。そんな書き方する人が悪いけど。

let mutは型の変更ができないので、使い方が違うのは理解

tigercat1124tigercat1124

複合型

  • タプル
  • 配列

タプル

fn main() {
    let tup = (500, 6.4, 1);

    let (x, y, z) = tup;

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

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_hundred = x.0;

    let six_point_four = x.1;

    let one = x.2;
}

配列型

  • ヒープリもスタックに置きたい時
  • 固定長の長さを持っていたい時

角かっこの中に初期値とセミコロン、そして配列の長さを与えることで、各要素に同じ値を持つように配列を初期化することができます。

fn main() {
let a = [3; 5];
}
tigercat1124tigercat1124

関数

文と式

文が並び、最後に式を置くか文を置くという形で形成されます。

ほう?

Rustは、式指向言語なので、 これは理解しておくべき重要な差異になります。
文とは、なんらかの動作をして値を返さない命令です。 式は結果値に評価されます。

x = y = 6と書いてRustにおいては、 そうは問屋が卸さないわけです。

卸さないのか。

今まで見かけてきた行と異なり、文末にセミコロンがついていないx + 1の行に気をつけてください。

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

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

Rustでは、関数の戻り値は、関数本体ブロックの最後の式の値と同義です。 returnキーワードで関数から早期リターンし、値を指定することもできますが、多くの関数は最後の式を暗黙的に返します。 こちらが、値を返す関数の例です:

なるほど。

fn five() -> i32 {
    5
}

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

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

関数の戻り値が必須の場合に、文を返却しようとするとエラーになるのか。returnで返した場合はある種、returnという文の処理になる?

エラー

fn plus_one(x: i32) -> i32 {
    x + 1;
}

OK

fn plus_one(x: i32) -> i32 {
    x + 1
}

//or 

fn plus_one(x: i32) -> i32 {
    return x + 1;
}

なんかTypescirptでいう即時関数的な印象を受けた。が、これはあくまでも関数ではなくてを返却していると認識しなければいけない。

let x = {let x = 6; x};
println!("The value of x is: {}", x);

// The value of x is: 6
tigercat1124tigercat1124

制御フロー

if文

コード内の条件式は、bool型でないといけません。

なるほど、TypeScriptみたいに値での確認はできないわけか。
と思ったら、言及されていた。

RubyやJavaScriptなどの言語とは異なり、Rustでは、論理値以外の値が、自動的に論理値に変換されることはありません。 明示し、必ずifには条件式として、論理値を与えなければなりません。

ifは式なので、let文の右辺に持ってくることができます。リスト3-2のようにですね。

以前学んだときにそんなこと言っていたの思い出した。関数のところで言及していた式として使えるのね。

let number = if condition { 5 } else { 6 };

ただし、if式全体は各アームによる型は一緒でなければならない。

Error

let number = if condition { 5 } else { "six" };

loop

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);
}

whileとfor

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

    while index < 5 {
        // 値は{}です
        println!("the value is: {}", a[index]);

        index += 1;
    }
}

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

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

Range型は、標準ライブラリで提供される片方の数字から始まって、 もう片方の数字未満の数値を順番に生成する型です。
forループと、まだ話していない別のメソッドrevを使って範囲を逆順にしたカウントダウンはこうなります:

fn main() {
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    println!("LIFTOFF!!!");
}

tigercat1124tigercat1124

【WIP】所有権

難関きました。

スタックとヒープ

一部学習済みなので、重要な点だけピックアップ

現代のプロセッサは、メモリをあちこち行き来しなければ、 より速くなります。

現代プロセッサに限らない気がするが。

どの部分のコードがどのヒープ上のデータを使用しているか把握すること、ヒープ上の重複するデータを最小化すること、 メモリ不足にならないようにヒープ上の未使用のデータを掃除することは全て、所有権が解決する問題です。

低レイヤーの言語話者なら頷ける気がする。(組み込み出身で良かった)