やりたいこと
- 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.toml
にdefault-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.rs
はfoo
クレートとして利用可能use foo;
- パッケージ名にハイフンが含まれている場合はアンダースコアに変換される
foo-bar
はfoo_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.rs
とlib.rs
は異なるクレートだから、crate::
が指し示すものも違う
ディレクトリ
src/sub_module/string.rs
を使いたい場合どうするかmod sub_module::string
のような書き方はできない
- ディレクトリと同名のファイルを作成してモジュールを定義する
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();
lib.rs
にて階層的にモジュールを定義する
- 推奨される方法かどうかはわからない
// lib.rs
pub mod sub_module {
pub mod string;
};
sub_module::string::trim();
use
と mod
の違い
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';
- 代数的データ型の利点
- 型レベルでプログラムの意図を記述することができる
- パターンマッチングによって簡潔に記述できる
- 一回関数型の言語触って学んだほうが早い気がしてきた