Rust学習メモ
これを読みながら気になったところを未来の自分のためにメモする。
1. 事始め
1.1. インストール
これに加えて、なんらかのリンカが必要になるでしょう。既にインストールされている可能性は高いものの、 Rustプログラムをコンパイルしようとした時、リンカが実行できないというエラーが出たら、 システムにリンカがインストールされていないということなので、手動でインストールする必要があるでしょう。 Cコンパイラは通常正しいリンカとセットになっています。 自分のプラットフォームのドキュメンテーションを見てCコンパイラのインストール方法を確認してください。 一般的なRustパッケージの中には、Cコードに依存し、Cコンパイラが必要になるものもあります。 ですので、Cコンパイラは今のうちにインストールしておく価値があるかもしれません。
Ubuntu on WSL2ではリンカのインストールが必要だった。
$ sudo apt install build-essential
2. 数当てゲームをプログラムする
(記載の通りやっていくだけ)
3. 一般的なプログラミングの概念
3.2. データ型
ここで型注釈を付けなければ、コンパイラは以下のエラーを表示し、これは可能性のある型のうち、 どの型を使用したいのかを知るのに、コンパイラがプログラマからもっと情報を得る必要があることを意味します
変数側に型注釈を入れる方法は分かったが、メソッド呼び出し側に型注釈を入れる方法が書いてない😇
調べたら、turbofish ::<>
というものを使うらしい。
let guess = "42".parse::<u32>().expect("Not a number!");
4. 所有権を理解する
4.1. 所有権とは?
メモリ安全性を保証するために、Rustにおいてこの場面で起こることの詳細がもう一つあります。 確保されたメモリをコピーしようとする代わりに、コンパイラは、s1が最早有効ではないと考え、 故にs1がスコープを抜けた際に何も解放する必要がなくなるわけです。s2の生成後にs1を使用しようとしたら、 どうなるかを確認してみましょう。動かないでしょう:
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
なるほど、常にownershipを持つものは1つだけということか。
うっかり下のようにやってしまいそう。(コンパイラがエラーを教えてくれる)
fn main() {
let s = String::from("hello");
print_str(s);
print_str(s); // err
}
fn print_str(str: String) {
println!("{}", str);
}
所有権を移してあげると解決する。
fn main() {
let s = String::from("hello");
let s = print_str(s);
print_str(s);
}
fn print_str(str: String) -> String {
println!("{}", str);
str
}
4.2. 参照と借用
4.1の例は借用でもっとシンプルになる。
fn main() {
let s = String::from("hello");
print_str(&s);
print_str(&s);
}
fn print_str(str: &String) {
println!("{}", str);
}
可変の参照は制約がある。
- 可変の参照はスコープ内では1つしか持てない
- 不変の参照が存在する場合は可変の参照は持てない
4.3. スライス型
例えば、"hello world"という文字列から先頭の単語を探す場合、
結果として"hello"ということがわかるが、
この"hello"は"hello world"の場合にのみ意味を持っているため、
あとから"great rust"などに書き換えてしまうと"hello"は正しい意味を成さなくなってしまう。
スライス型を使って表現することで、もとのデータ型への参照を持つ形となるため、
不変の参照が存在する場合は可変の参照は持てない
の制約を利用してもとのデータとの関連付けができる。
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear(); // sはwordに不変参照されている
println!("the first word is: {}", word);
}
5. 構造体を使用して関係のあるデータを構造化する
5.1. 構造体を定義し、インスタンス化する
<b>構造体更新記法</b>というものを使って、ちょっと違うインスタンスを作れる。
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
let user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername567"),
..user1
};
5.2. 構造体を使ったプログラム例
println!
マクロに指定するフォーマットの {}
は与えられた引数をDisplayトレイトの実装に従って変換し出力する。
しかし、構造体にはDisplayトレイトの実装はない。なぜなら、出力したい内容は構造体の役割によって千差万別だから。
これを自分で実装してもいいが、デバッグしたいだけなら {:?}
を使ったほうが楽。
これはDebugトレイトの実装に従って変換し出力する。
Debugトレイトを自分で実装してもいいが、derive注釈を使うと便利。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {:?}", rect1);
}
6. Enumとパターンマッチング
Swiftっぽいと思った。
7. 肥大化していくプロジェクトをパッケージ、クレート、モジュールを利用して管理する
7.1. パッケージとクレート
- クレートにはバイナリとライブラリの2種類がある
- パッケージには1つ以上のクレートを持っていないとダメ
- パッケージには2つ以上のライブラリクレートを持たせることはできない
7.2. モジュールを定義して、スコープとプライバシーを制御する
クレートの中をグループ化する単位としてモジュールがある。
7.3. モジュールツリーの要素を示すためのパス
絶対パスと相対パスがあるみたいだけど、相対パスはいつ使うんだろう?🤔
IDEのリファクタ機能の恩恵を受けるなら、絶対パスだけで事足りそうな気がする。
8. 一般的なコレクション
8.2. 文字列でUTF-8でエンコードされたテキストを保持する
文字連結には +
演算子ではなく format!
マクロを使ったほうがいい。
+
は内部的に add
メソッドを呼び出しているが、そのメソッドはレシーバーの所有権を取得するので、
呼び出すとレシーバーは使用できなくなる。
他の多くのプログラミング言語では、文字列中の文字に、添え字で参照してアクセスすることは、有効なコードであり、 一般的な処理です。しかしながら、Rustにおいて、添え字記法でStringの一部にアクセスしようとすると、 エラーが発生するでしょう。
添字アクセスはバイト値アクセスのため、2バイト以上で文字となるUTF-8を正しく扱えない可能性があるから。