やりたいこと

  • Tauri でプロトタイプを完成させる
    • ファイルを読み込む方法を調べる
    • Rust で SQLite に保存する方法を調べる
  • react-virtualized で表示領域外の要素を描画しないようにする
  • Effect の勉強

意識

  • 落ち着いて考えればわかること
  • より良いものの見方を見つけてそこに集中する
  • 流れを感じること
  • 失敗したとき、どうすればよいのかが明らかになる
  • 失敗を繰り返して成長していく
  • 外からの見え方は気にしないこと
    • それは信頼できないし、多種多様すぎてそもそも全ての人に好かれるなど不可能
  • 自分は自分らしくいればいい
  • 自分らしくいて、自分に余裕のあるときだけ、周りに与えることができる
  • もし私が外の何かを気にするとき、私はわたしの流れを失ってしまう
  • それは私らしさではないし、そのとき周りに与えられるものはなにもない
  • 私はわたしらしくいたいし、私は私らしくいるときにだけ、周りに与えることができる

学び・気付き

  • Rust のモジュール周りについて理解が深まった

Tauri / Rust

  • Tauri でグローバルなステートを定義するには Builder::manage を使う
use tauri;
 
struct Foo {
    value: i32
}
 
#[tauri::command]
fn get_value(arg: i32, foo: tauri::State<Foo>) -> i32 {
    arg + foo.value
}
 
fn main() {
    tauri::Builder::default()
        .manage(Foo { value: 42 })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
 

Rust の SQLite クライアント


  • クエリで細かいことしそうだから ORM じゃないほうが良さげ
  • rusqlite がシンプルで良さそう

  • Tauri に SQL のプラグインがあったからそっち使ったほうが簡単かも
  • このプラグインはフロントから SQL を叩くためのものだった

  • 現状の方針としては、rusqlite を Rust 側で使って IPC でデータのやり取りをする
  • 将来的にバックグラウンドタスクでメタデータを付与することを考えるとこの構成が良い

Rust に慣れる

  • モジュールあたりがよくわかってないからファイルが上手く分割できず開発効率が悪いからちゃんと調べる

基本

基本的な構成

foo
├── Cargo.toml
└── src
    └── main.rs

cargo build すると target/debug/foo に実行可能ファイルが生成される

foo
├── Cargo.toml
└── src
    └── main.rs
└── target
    └── debug
        └── foo

cargo run するとビルドしたうえで実行される

エントリーポイント

  • デフォルトのエントリーポイントは src/main.rs
  • エントリーポイントは main 関数をもつ必要がある
  • src/my-bin.rs をエントリーポイントにしたい場合は Cargo.toml[[bin]] を追加する
[[bin]]
name = "my-bin"
path = "src/my-bin.rs"
  • 実行には --bin オプションを指定するか Cargo.tomldefault-run を追加する
$ cargo run --bin my-bin

or

[package]
...
default-run = "my-bin"
  • [[bin]] を追加したうえでオプションを指定せず cargo run だけするとエラーが出る
error: `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key.

バイナリクレートとライブラリクレート

  • バイナリクレート (main.rs)
    • コンパイル時に実行可能ファイルが生成される
    • 利用: コンソールや GUI から直接実行
  • ライブラリクレート (lib.rs)
    • 他のクレートから利用される関数や構造体を定義・公開する
    • 利用: 他のクレートから use して利用
    • main 関数を持たない
    • src/lib.rs がデフォルトのライブラリクレート
      • デフォルトではライブラリ名はパッケージ名になる
        • 例: foo/src/lib.rsfoo クレートとして利用可能
          • use foo;
        • パッケージ名にハイフンが含まれている場合はアンダースコアに変換される
          • foo-barfoo_bar クレートとして利用可能
      • Cargo.toml[lib] を追加することで変更可能

モジュール

  • main.rs から直接 foo モジュールを使うこともできるし、lib.rs を経由して使うこともできる

直接

// src/main.rs
// モジュールの定義
// src/foo.rs をモジュールとして定義
mod foo;
 
fn main() {
    foo::greet("Alice");
}

lib.rs 経由

// src/main.rs
// mod ではなく use で lib.rs に定義されているモジュールを使う
use foo::greet
 
fn main() {
    greet("Alice");
}
// src/lib.rs
// ./src/foo.rs をモジュールとして定義して公開
pub mod foo;

crate::

  • crate はクレートのルートを指す
  • main.rslib.rs は異なるクレートだから、crate:: が指し示すものも違う

ディレクトリ

  • src/sub_module/string.rs を使いたい場合どうするか
    • mod sub_module::string のような書き方はできない
  1. ディレクトリと同名のファイルを作成してモジュールを定義する
foo
├── Cargo.toml
└── src
    └── main.rs
    └── lib.rs
    └── sub_module
        └── string.rs
    └── sub_module.rs
// src/sub_module.rs
pub mod string;
// src/lib.rs
mod sub_module;
 
// 使う
sub_module::string::trim();
  1. lib.rs にて階層的にモジュールを定義する
  • 推奨される方法かどうかはわからない
// lib.rs
pub mod sub_module {
    pub mod string;
};
 
sub_module::string::trim();

usemod の違い

  • mod は新しいモジュールを定義する
  • use は定義済みのモジュールを使う
  • mod foo; としたとき、自動的に ./foo.rs または ./foo/mod.rs を探す
  • mod foo { } とするとモジュールを直接定義できる
mod foo {
    fn greet(name: &str) {
        println!("Hello, {}!", name);
    }
}
  • mod foo { mod bar } のようにモジュールは入れ子にできる
mod foo {
    mod bar {
        pub fn greet(name: &str) {
            println!("Hello, {}!", name);
        }
    }
}

Effect

Effect について勉強

  • Effect の基本となる型は Effect
    • Effect<A, E, R>
      • A: 成功時の値
      • E: エラー時の値
      • R: 依存
const fetchSample = (id: string):
  Effect<Sample, NotFound | NetworkError> => { }
  • エラーで何が起こりうるのかを明示的に書くことができる
  • また、R に依存を指定することで、fetchSample するために必要な依存関係を明示的に示すことができる
const fetchSample = (id: string):
  Effect<
    Sample,
    NotFound | NetworkError,
    // データベースサービスを使ってデータを取得している
    DatabaseService
  > => {}

  • 代数的データ型 (Algebraic Data Type: ADT)
    • 直積型と直和型の2つのタイプを組み合わせて作られる型
// 直積型
type Person = {
  name: string;
  age: number;
}
 
// 直和型
type Color = 'Red' | 'Green' | 'Blue';
  • 代数的データ型の利点
    • 型レベルでプログラムの意図を記述することができる
    • パターンマッチングによって簡潔に記述できる
  • 一回関数型の言語触って学んだほうが早い気がしてきた

参考

Haskell