Rust学習
The Rust Programming Language 日本語版を読み込もう
cargo
new
cargo new xxxx
build
cargo build
target/debug
に作成される
cargo build --release
target/release
のディレクトリに作成される。
run
cargo run
check
cargo check
ビルドをしないコンパイルができるので、コンパイルエラーの確認に役に立つ
数当てゲーム
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で抜ける
可変と不変
シャドーイングできちゃうから、mutじゃなくても書けそう。
let num = "-5";
println!("num is {}",num);
let num = 5;
println!("num is {}",num);
ちゃんと言及されていた。
シャドーイングは、変数をmutにするのとは違います。なぜなら、letキーワードを使わずに、 誤ってこの変数に再代入を試みようものなら、コンパイルエラーが出るからです。letを使うことで、 値にちょっとした加工は行えますが、その加工が終わったら、変数は不変になるわけです。
不変ではあるけど、let使いまくったらじゃないじゃんとも思った。そんな書き方する人が悪いけど。
let mutは型の変更ができないので、使い方が違うのは理解
型
複合型
- タプル
- 配列
タプル
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];
}
関数
文と式
文が並び、最後に式を置くか文を置くという形で形成されます。
ほう?
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
コメント
他の言語と一緒!! 以上!!
制御フロー
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!!!");
}
【WIP】所有権
難関きました。
スタックとヒープ
一部学習済みなので、重要な点だけピックアップ
現代のプロセッサは、メモリをあちこち行き来しなければ、 より速くなります。
現代プロセッサに限らない気がするが。
どの部分のコードがどのヒープ上のデータを使用しているか把握すること、ヒープ上の重複するデータを最小化すること、 メモリ不足にならないようにヒープ上の未使用のデータを掃除することは全て、所有権が解決する問題です。
低レイヤーの言語話者なら頷ける気がする。(組み込み出身で良かった)