Rust learning notes
Rust のマクロ
#[macro_use]
とは?
The #[macro_use]
attribute is used to indicate that a crate or module wants to use macros from another crate.
#[macro_use]
extern crate my_macros;
fn main() {
my_macro!(); // Using a macro from the `my_macros` crate
}
Rust の trait について
【確信未だ無し】 struct の trait 実装判断はフィールドに依る(?)
pub struct MyError {
field1: u32,
field2: String,
}
上記のような構造体があるとき、field1
とfield2
はそれぞれu32, String 型でどちらも Send
trait を実装しているので、MyError
構造体もSend
trait を実装している、ことになるらしい。(ChatGTPに聞いたらそう答えた)
以下の公式Bookを読んでも記述が見当たらないように見える(2023年6月12日)
type XXX;
とは?
trait にある 例えば Iterator
trait には以下のようにtype Item;
がある。
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
これは Associated type と呼ばれるRustの仕様で、traitが持つ型パラメータ とみなしてよい。
そういう意味ではジェネリクスと同じようだが、何が違うかというと、traitを実装する構造体にてジェネリクスでは1つの名前で複数の型で定義できてしまうが、Associated typeならばある構造体にて1つの型で固定し、いちいち型を指定する必要がなくなる。
// ジェネリクスでの定義
pub trait Iterator<T> {
fn next(&mut self) -> Option<T>;
}
// ジェネリクスでは以下のように複数の型で実装できてしまうが、
impl Iterator<String> for Counter {...
impl Iterator<u32> for Counter {...
// Associate TypeならばCounter構造体のIterator実装は1つの型に絞ることができる。
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
// --snip--
公式Doc対応ページ
non-exhaustive patterns
とは?
エラー non-exhaustive patterns というエラーはmatchのパターンマッチ等にて羅列する条件が網羅されておらず、値が返せない(定義されていない)ためにコンパイルエラーになっている。
英単語 exhaustive は "including or considering all elements or aspects; fully comprehensive." という意味。
例
error[E0004]: non-exhaustive patterns: `XXX` not covered
if let Some(x) = XXX {
の理解
let
は文法仕様として以下である。
let PATTERN = EXPRESSION;
ポイントはPATTERN
であり let x = 5;
は厳密にはx
というパターンに対して束縛(bind)させるのがlet
の役割である。
let (x, y) = (3, 4, 5); // mismatched types ← (_, _) タプル型に合っていない
let Some(x) = Some(5); // refutable pattern in local binding ← Noneのパターンがサポートされていない(と論破可能)
refutable pattern を回避するために if let
を使う。Someのパターンの場合、直下のスコープに入る。
// コンパイルは通る
if let Some(x) = Some(5) {
println!("{}", x); // 5
}
Trait object (動的ディスパッチ) vs Generics (静的ディスパッチ)
Effective Rustによると、Genericsの方が望ましいが、Trait Objectを利用しないといけない場面は多そう。
仮にTraitを実装できていても、Genericsの型へプログラムスコープ内で変換することができない説。
→ 自コード具体例
__TODO: __ 詳細を整理したい
trait Shape {
fn area(&self) -> f64;
}
// trait object (動的ディスパッチ)
fn print_area(shape: &dyn Shape) {
println!("Area: {}", shape.area());
}
// generics (静的ディスパッチ)
fn print_area<T: Shape>(shape: T) {
println!("Area: {}", shape.area());
}
fn main() {
// 動的ディスパッチ呼び出し
print_area(&rectangle);
// 静的ディスパッチ呼び出し
print_area(rectangle);
}
Rust の並行処理を理解したい (ずっと)
Arc, Mutex, Condvar を組み合わせた Example でそれぞれを理解
Condvarはこの例のようにMutexと組み合わせて利用されるもの
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = Arc::clone(&pair); // 【重要】Arc::clone によって作られるpair2はpairと同じ実態(同メモリ上)を参照する
// Inside of our lock, spawn a new thread, and then wait for it to start.
thread::spawn(move|| {
let (lock, cvar) = &*pair2;
let mut started = lock.lock().unwrap(); // 【重要】Mutex (ここでは`lock`)はある値がそのとき1スレッドのみから変更される制御のためのもの
*started = true;
// We notify the condvar that the value has changed.
cvar.notify_one(); // 【重要】Condvarはスレッド間でno CPU時間で"待ち"→"開始OK"を伝達させるためのもの。
});
// Wait for the thread to start up.
let (lock, cvar) = &*pair; // 上記別スレッドでの&*pair2と同じ実態(同メモリ上)である。
let mut started = lock.lock().unwrap();
while !*started {
started = cvar.wait(started).unwrap(); // 【重要】ここで一時的にstartedのLockを解除し、cvar.notify_one() を待つ。
// 【重要】この Condvar.wait(&self, MutexGuard) は引数のMutexのLockを外して待ち通知が来てwaitがreturnを返すときには"再度Lockを取得する"
}
Rust の所有権、参照、借用(borrow) を理解したい (ずっと)
Playground メモ1: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2403391b1fc8615e7cb6c16c8506711b
Rust のエラーハンドリング
?
は即時returnの糖衣構文
?
演算子は Result<T, E>
またはOption<T, None>
を返す関数内で Result<T, E>
またはOption<T, None>
に対応する関数を呼び出したときに即時returnの記述を簡潔化するための記法である。
以下のコードは
fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = File::open("hello.txt")?;
.
.
Ok(something)
}
以下のコードへコンパイラが変換するとみなして良い。(厳密にはエラー型の扱いが異なる可能性あり ※)
fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = match File::open("hello.txt") {
Ok(file) => file,
Err(e) => { return Err(e); }
}
.
.
Ok(username)
}
Resultを返す関数内でOptionを返す関数を呼び出し?
を使えば以下のコンパイルエラーになる。
error[E0277]: the `?` operator can only be used in a function that returns `Result`
※ https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html
Rust のモジュール について
基本の基
The bookの以下がすべてである。
- ルートが1つだけ存在する
-
mod xxx;
がないとxxx.rs
はモジュールとして認められない。( ただし mod xxx {} で囲っていればOK ) - 同じルート内では
use
はただのショートカット(エイリアス)でありmod参照の上に成り立っている
pub use について
use moduleA::moduleB::moduleC::MethodD;
のように利用者が知る必要のないmoduleB,Cを知らなくて良いようにする仕組み。
pub use self::kinds::SecondaryColor;
のようにすることで
use art::PrimaryColor;
と書けるようになる。
Rust の型
type struct AA(pub u8, pub u8);
は Tuple Structs という名前が付いたタプル型
Eq と Hash trait を実装することで Hashのキーになることができる。
100 Exercises To Learn Rust やりたい