The Rust Programming Language をやってみる
数あてゲームまでは終わった!!
本当にやっただけなので、知らなかったところはアウトプットしておきます。
Cargoって?
3行で話すと
- Rustのビルドシステム兼パッケージマネージャ
- どのOSでもコマンドが同じ(ex:
cargo ~~
) -
cargo doc --open
をすると、ローカルで使用中のクレートのドキュメントをまとめたものを生成してくれる
参考
CargoはRustのビルドシステム兼パッケージマネージャです。 ほとんどのRustaceanはこのツールを使ってRustプロジェクトを管理しています。
argo check
は実行ファイルを生成するステップを省くことができるので、多くの場合、cargo build
よりもずっと高速です。 もし、あなたがコードを書きながら継続的にチェックするのなら、cargo check
を使えば、そのプロセスを高速化できます! そのため多くのRustaceanはプログラムを書きながら定期的にcargo check
を実行し、コンパイルできるか確かめます。 そして、実行ファイルを使う準備ができたときにcargo build
を走らせるのです。
Cargoを使用するもう一つの利点は、どのOSで作業していてもコマンドが同じであることです。 そのため、これ以降はLinuxやmacOS向けの手順と、Windows向けの手順を分けて説明することはありません
これもRustを使ってて楽しいポイントの一つかも。
cargo run
コマンドを使って、この「Hello, world!」プログラムのコンパイルと実行を一気に行いましょう。
このゲーム(の開発)では各イテレーションを素早くテストしてから、次のイテレーションに移ります。
run
コマンドは、今回のようにプロジェクトのイテレーションを素早く回したいときに便利です。イテレーションとは開発工程の「一回のサイクル」のことで、サイクルには、設計、実装、テスト、改善(リリース後の振り返り)が含まれます。 アジャイル開発ではイテレーションを数週間の短いスパンで一通り回し、それを繰り返すことで開発を進めていきます。
この章では「実装」→「テスト」のごく短いサイクルを繰り返すことで、プログラムに少しずつ機能を追加していきます。
Cargoのもう一つの素晴らしい機能は、
cargo doc --open
コマンドを走らせると、すべての依存クレートが提供するドキュメントをローカルでビルドして、ブラウザで開いてくれることです。 たとえばrand
クレートの他の機能に興味があるなら、cargo doc --open
コマンドを実行して、左側のサイドバーにあるrand
をクリックしてください。
実際に生成して閲覧してる図↓
すごい〜〜!
自分が作っているプロジェクトについても一応載せてくれる。
The bookで言及されていたrand
。
Python使ってたところから見たRustの好きなとこ
エラーがとにかくわかりやすい
typoまで検知してhelpで出してくれる
前からあるのかもしれないけど、Rustの好きなとこ、こういうとこです...ガッツリtypoしてるけど、それに対して「多分これじゃない?」って提示してくれるとこ...
(他の言語でもあるのかもしれないけど)大変好きです...嬉しい。。。
式と文の違い
- 式:
- セミコロンが終わりにない。
- 文:
- セミコロンが最後につく。
- 値を返さない。
式は終端にセミコロンを含みません。式の終端にセミコロンを付けたら、文に変えてしまいます。そして、文は値を返しません。 次に関数の戻り値や式を見ていく際にこのことを肝に銘じておいてください。
所有権ちょっと難しいけど、ふんわりそういうのがあるんだなあと認識しておこう
- Rustの各値は、所有者と呼ばれる変数と対応している。
- いかなる時も所有者は一つである。
- 所有者がスコープから外れたら、値は破棄される。
参照もまだちょっと難しい感じ。ふんわり知っておこう。
&による参照の逆は、参照外しであり、参照外し演算子の*で達成できます。
&s1という記法により、s1の値を参照する参照を生成することができますが、これを所有することはありません。 所有してないということは、指している値は、参照がスコープを抜けてもドロップされないということです。
参照について議論したことを再確認しましょう:
- 任意のタイミングで、一つの可変参照か不変な参照いくつでものどちらかを行える。
- 参照は常に有効でなければならない
整数のようなコンパイル時に既知のサイズを持つ型は、スタック上にすっぽり保持されるので、 実際の値をコピーするのも高速だからです。これは、変数yを生成した後にもxを無効化したくなる理由がないことを意味します。 換言すると、ここでは、shallow copyとdeep copyの違いがないことになり、 cloneメソッドを呼び出しても、一般的なshallow copy以上のことをしなくなり、 そのまま放置しておけるということです。
どの型がCopyなのでしょうか?ある型について、ドキュメントをチェックすればいいのですが、 一般規則として、単純なスカラー値の集合は何でもCopyであり、メモリ確保が必要だったり、 何らかの形態のリソースだったりするものはCopyではありません。
- あらゆる整数型。u32など。
- 論理値型であるbool。trueとfalseという値がある。
- あらゆる浮動小数点型、f64など。
- 文字型であるchar。
- タプル。ただ、Copyの型だけを含む場合。例えば、(i32, i32)はCopyだが、 (i32, String)は違う。
なんとなくはわかったけど、&str
とString
の場合で挙動が異なるのはなぜかわかっていないかも ...
fn main() {
let s1 = String::from("hello"); // String
let s2 = s1; // String
let x = "hello"; //&str
let y = x; // &str
println!("x={}, y={}", x, y);
// OK
println!("s1={}, x={}, y={}", s1, x, y);
// s1込みだとerror[E0382]: borrow of moved value: `s1` となる。
}
わかっていること
-
String::from()
で呼び出して変数の値を定義する場合(s1, s2
)と、そのままダブルクォーテーションで囲むだけで変数の値を定義する場合(x, y
)だと挙動がかわる
分からないこと
-
String
と&str
の違い、挙動の制限の違い
所有権と関数のところで、String::from()
で変数を定義した場合と、
文字列そのまま定義した場合で型が違うし挙動も違う...👀
それぞれの型のドキュメントみた方が良いかも、、、
fn main() {
let s = "hello";
// let s = String::from("hello");
println!("{}", s);
takes_ownership(s);
println!("{}", s);
let x = 5;
println!("{}", x);
makes_copy(x);
println!("{}", x);
}
fn takes_ownership(some_string: &str) {
println!("{}", some_string);
}
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}
結果。
hello
hello
hello
5
5
5
Rustの文字列理解したい
基本的に型が違えば別物、と思おう。慣れていくぞーーー
参照
ここの関数呼び出しについて、もっと詳しく見てみましょう:
let s1 = String::from("hello"); let len = calculate_length(&s1);
この
&s1
という記法により、s1の値を参照する参照を生成することができますが、これを所有することはありません。 所有してないということは、指している値は、参照がスコープを抜けてもドロップされないということです。
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The lentgh '{}' is {}.", s1, len); // 結果:The lentgh 'hello' is 5.
}
fn calculate_length(s: &String) -> usize {
s.len()
}
借用とは:
関数の引数に参照を取ることを借用と呼びます。
fn calculate_length(s: &String)
のような場合は借用、ってことかな
参照について議論したことを再確認しましょう:
- 任意のタイミングで、一つの可変参照か不変な参照いくつでものどちらかを行える。
- 参照は常に有効でなければならない。
構造体
定義された各構造体は、構造体内のフィールドが同じ型であっても、それ自身が独自の型になります。