僕のRust チートシート
自分のためのチートシートを作ります。 このチートシートを少しずつ育てていくことにする。
- コマンド
- 調べる
- サードパーティクレート
- 小技
- main 関数からは Ok / Err を返せる
- Resultが返る関数においてErrが返ったら即リターンする?構文
- Vec<i32>の合計値を求めようとして苦労した
- 整数同士で割り算するなら先に as f64 しておく
- 使用しない戻り値は_に代入する
- Mac でビルド成果物を実行する
- 標準ライブラリで最も一般的なスマートポインタ
- Rc::clone の使い所
- Interior mutability pattern(インテリアミュータビリティパターン)
- Rc と RefCell の合わせ技
- MPSC
- 連番のVecを作る
- スレッド間で値を安全に共有するためのAtomic Reference Counting, Arc.
- 等価比較する
- impl 内の fn の引数で self: Box<Self> になってるのなに
- パターンがマッチし続ける限りwhileループを実行することができる while let
- イテレータの中身をindexとともに取得したい時は enumerate() を使う
- Vec<u32>.iter().map(|n| *n++) のように n は dereference が必要?
- collect() を変数に格納せずに型指定する
- お手軽にコピーしたいときは
- テストで println! を標準出力する
- Rust の Vec をインデックス付きでループする方法
- JS でいうところの array.some
- Rust の補完が効かない問題
コマンド
command | 説明 |
---|---|
cargo new hello_world |
バイナリプロジェクトを作る |
cargo new --lib hello_world |
ライブラリプロジェクトを作る |
cargo search hello_world |
クレートのバージョンについて調べる |
調べる
知りたいこと | 参考URL |
---|---|
vecの使い方について | 公式ドキュメント |
arc | TRPL |
サードパーティクレート
クレート名 | 説明 | 参考URL |
---|---|---|
structopt | コマンドライン引数をいい感じに受け取る | MS Learn |
chrono | 日付と時刻のデータを処理する | MS Learn |
slice_group_by | vec内でグループを作る時に使うと便利。group-by は公式に実装される予定がある。 | docs.rs |
serde | 型から Serialize と Deserialize の特性を派生できるようにするベース クレート | MS Learn |
serde_json | Serialize と Deserialize の特性を、選択したファイル仕様形式である JSON に実装するクレート | MS Learn |
home | ユーザーのオペレーティング システムによって異なるHOMEディレクトリを判断する | MS Learn |
anyhow | 有用で適切なエラーをユーザーに表示できる | MS Learn |
小技
main 関数からは Ok / Err を返せる
fn main() -> Result<(), ()> { Ok(()) }
exeにコンパイルした場合、Ok(())
をリターンすると実行結果は0
となり、Err(())
をリターンすると実行結果は1
となる。
随所に出てくる不自然な()
は、空のタプル。voidのようなものだろうか。
Resultが返る関数においてErrが返ったら即リターンする?
構文
use std::error::Error; use std::fs::File; fn main() -> Result<(), Box<dyn Error>> { let f = File::open("hello.txt")?; Ok(()) }
Vec<i32>
の合計値を求めようとして苦労した
fn sum(list: &Vec<i32>) -> i32 { list.iter().as_slice().iter().sum::<i32>() }
整数同士で割り算するなら先に as f64 しておく
fn mean(list: &Vec<i32>) -> f64 { list.iter().as_slice().iter().sum::<i32>() as f64 / list.len() as f64 }
使用しない戻り値は_
に代入する
生真面目にlet result =
みたいに書くと警告吐かれて困るので。
// _ なら使わなくても警告吐かれない。 let _ = std::fs::remove_file("memo.txt");
Mac でビルド成果物を実行する
恥ずかしながら僕にはわからなかった。 ビルド成果物があるディレクトリに移動して・・・👇
./hello_world
./
がない場合、コマンドなしの結果になる。
実行ファイルを実行するには ./
が必要。
標準ライブラリで最も一般的なスマートポインタ
Box<T>
ヒープ上にメモリを確保するスマートポインタRc<T>
多重所有を可能にする参照カウント型スマートポインタRef<T>
RefMut<T>
コンパイル時ではなく実行時に借用規則を実施する型であるRefCellを通してアクセスされるスマートポインタ
Rc::clone の使い所
あんまりよくわからないけど、グラフデータなどで複数のノードから一つのノードを参照するような構造を作る際に使える。通常のBox(再帰は可能)などで実現しようとすると所有権の問題でコンパイルできない。それを解決するのにDeepClone しちゃうと性能の問題が発生するし、全く別のインスタンスを参照することになってしまう。
そんな場合に Rc::new で唯一のノードを作成し、それを参照する複数のノードは唯一のノードを引数に Rc::clone することで、同一インスタンスへの参照を持つことができる。
Interior mutability pattern(インテリアミュータビリティパターン)
std::cell::RefCell の使い所。 Mock オブジェクトを使って試験したい場合の例がわかりやすい。
Rc と RefCell の合わせ技
もうなんだかわからん。
MPSC
multiple producer, single consumer の略。
連番のVecを作る
fn main() { let min = 8; let max = 13; let vec: Vec<u8> = (min..max).collect(); println!("{:?}", vec); // [8, 9, 10, 11, 12] }
スレッド間で値を安全に共有するためのAtomic Reference Counting, Arc.
引用:
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap()); }
等価比較する
/// derive で PartialEq を指定すると、プロパティ全てを等価比較する挙動となる。 #[derive(PartialEq, Debug)] struct A { x: u8, y: u8, } /// 独自の実装が必要であれば derive で PartialEq を指定せず impl で fn eq を実装する。 #[derive(Debug)] struct B { id: u8, foo: u8, } impl PartialEq for B { /// id が同じならば等価とみなす fn eq(&self, other: &Self) -> bool { self.id == other.id } } fn main() { let a1 = A { x: 1, y: 2 }; let a2 = A { x: 1, y: 2 }; let a3 = A { x: 3, y: 3 }; assert_eq!(a1, a2); assert_ne!(a1, a3); let b1 = B { id: 1, foo: 1 }; let b2 = B { id: 1, foo: 2 }; let b3 = B { id: 3, foo: 1 }; assert_eq!(b1, b2); assert_ne!(b1, b3); }
詳しくは👉 Eq in std::cmp - Rust
impl 内の fn の引数で self: Box<Self>
になってるのなに
いやぁそろそろわけわからなくなってきましたが、説明にはこうありました。
メソッドの最初のパラメータとして、self、&self、&mut selfではなく、self: Box
となっていることに注意してください。この構文は、その型を持つBoxに対してメソッドが呼び出されたときのみ有効であることを意味します。この構文は、Box の所有権を取得し、古い状態を無効にして、Postの状態値が新しい状態に変換できるようにします。
Box に格納している場合のみ呼び出し可能な関数を定義する際に使うのだろうか。 とても使い所が難しい気がする。
参考👇
Implementing an Object-Oriented Design Pattern - The Rust Programming Language
パターンがマッチし続ける限りwhileループを実行することができる while let
if letと似た構造のwhile let条件付きループは、パターンがマッチし続ける限りwhileループを実行することができます。リスト18-2の例は、ベクターをスタックとして使用し、ベクター内の値をプッシュされたのと逆の順序で表示するwhile letループを示しています。
let mut stack = Vec::new(); stack.push(1); stack.push(2); stack.push(3); while let Some(top) = stack.pop() { println!("{}", top); }
引用元👇
All the Places Patterns Can Be Used - The Rust Programming Language
イテレータの中身をindexとともに取得したい時は enumerate()
を使う
let v = vec!['a', 'b', 'c']; for (index, value) in v.iter().enumerate() { println!("{} is at index {}", value, index); }
参考👇
All the Places Patterns Can Be Used - The Rust Programming Language
Vec<u32>.iter().map(|n| *n++)
のように n は dereference が必要?
iter_into() を使うと dereference は不要?
collect() を変数に格納せずに型指定する
assert_eq!( vec![1, 2, 3].iter().map(|n| *n * 2).collect::<Vec<u8>>(), &[2, 4, 6] );
お手軽にコピーしたいときは
#[derive(Copy, Clone)]
テストで println!
を標準出力する
以下でテストを実行することで標準出力されるようになる。
cargo test -- --nocapture
Rust の Vec をインデックス付きでループする方法
引用元:Rust の Vec をインデックス付きでループする方法 参考にさせて頂きます。ありがとうございます。
let a = ['a', 'b', 'c']; for (i, val) in a.iter().enumerate() { println!("{}: {}", i, val); }
👇 (0_i32..)
のような書き方もできるのか〜びっくり。
let a = ['a', 'b', 'c']; for (i, val) in (0_i32..).zip(a.iter()) { println!("{}: {}", i, val); }
勉強になります。
JS でいうところの array.some
some ではなく any でした。
let a = ['a', 'b', 'c']; let result = a.any(|item| item == 'a');
Rust の補完が効かない問題
以下と rust-analyzer があれば良さそう。
rustup component add rust-src rustup component add rust-analysis rustup component add rls