知らんけど。

主にプログラミングについて書きます

僕の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 することで、同一インスタンスへの参照を持つことができる。

参考👇 doc.rust-lang.org

Interior mutability pattern(インテリアミュータビリティパターン)

std::cell::RefCell の使い所。 Mock オブジェクトを使って試験したい場合の例がわかりやすい。

doc.rust-lang.org

Rc と RefCell の合わせ技

もうなんだかわからん。

doc.rust-lang.org

MPSC

multiple producer, single consumer の略。

doc.rust-lang.org

連番の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.

引用:

doc.rust-lang.org

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