Homework diary 📔
2023年5月に開催されたMotoko Bootcamp Day 2のプロジェクトをRust言語で実装します。
1. Rustプロジェクト作成
Rustのプロジェクト「day2」を作成します。cargo newコマンドを--libオプションを付与して実行します。
$ cargo new day2 --lib
$ cd day2生成されたファイルは以下の通りです。
day2
├── Cargo.toml
└── src
    └── lib.rs2. Cargo.tomlの編集
(1) IC関連ライブラリ追加
ic-cdkライブラリを使用します。最新バージョンでよいかと思いますので、以下のように実行ます。
$ cargo add candid ic-cdk serdecandid、serdeは、CandidType, Deserialize指定に必要?
(2) crate-type設定
Canister上から関数が正しく呼び出させるようcrate-typeをcdylibにします。
[lib]
crate-type = ["cdylib"]3. dfx.jsonの作成
Canisterの定義を行います。
{
  "canisters": {
    "day2": {
      "candid": "./day2.did",
      "package": "day2",
      "type": "rust"
    }
  },
  "defaults": {
    "build": {
      "args": "",
      "packtool": ""
    }
  },
  "version": 1
}4. candidの作成
dfx.jsonの [canisters] > [day2] > [candid]項目に指定したファイルに、Canisterに配置するDappが提供する関数のI/Fを定義します。
Motoko Bootcamp Day2 📺 Interfaceに相当するcandidを用意します。
type Time = int;
type Homework = record {
    "title": text;
    "description": text;
    "dueDate": Time;
    "completed": bool;
};
type ResultHomework = variant { Ok: Homework; Err: text };
type ResultOnly = variant { Ok; Err: text };
service: {
    addHomework: (Homework) -> (nat);
    getHomework: (nat) -> (ResultHomework) query;
    updateHomework: (nat, Homework) -> (ResultOnly);
    markAsCompleted: (nat) -> (ResultOnly);
    deleteHomework: (nat) -> (ResultOnly);
    getAllHomework: () -> (vec Homework) query;
    getPendingHomework: () -> (vec Homework) query;
    searchHomework: (text) -> (vec Homework) query;
}Time型
MotokoのTimeはint (System time is represent as nanoseconds since 1970-01-01.)のようで、Rust言語ではint128でOKと思われます。
Result型
Local caniterに配備してCandid UIで結果を見ると、motokoのResult.Resultはvariant {ok:xxx, err:text}(先頭小文字)となりますが、RustのResult<T, E>はvariant {Ok:xxx, Err:text} (先頭大文字)の違いがあるようです。
そのため、candid定義を Motoko に合わせて、variant { ok:Homework; err: text }とするとRust側でResultを返す際にマッピングエラーとなってしまい、Motoko Bootcamp Day 2と完全一致とはなりません。
2023/06/23追記:
tokuryoo氏より教えていただいた情報より、MotokoのResultに合わせるためにはRust標準のstd::Resultを使用せず、Motokoに合わせてenum型を用意する必要があるようです。
本ドキュメントではstd::Result型を使用する方法説明しますが、MotokoとI/Fを合わせる場合には、tokuryoo氏の内容が参考になります。
https://github.com/tokuryoo/motokobootcamp-rust-tokuryoo/tree/main/day2/src/day2_backend
5. lib.rsの編集
cargo newコマンドで生成されたlib.rsの中身をクリアして、day2用のプログラムを作成します。
Motoko Bootcamp Day 2と同じように、以下の関数を実装します。
- addHomework() 
- getHomework() 
- updateHomework() 
- markAsCompleted() 
- deleteHomework() 
- getAllHomework() 
- getPendingHomework() 
- searchHomework() 
Rust言語仕様の理解が十分でないため、作成したソースコードは所有権まわりをはじめ最適化されていない可能性がありますのでご注意ください。もしも、おかしな実装等が見つかりましたらが、ご指摘いただけますとさいわいです。
6. Unitテスト
Rustではソース内にUnitテストコードを含めて記述することができます。
TODO: 今回の範囲ではロジックにIC色は無いため、UnitテストはLocal canisterに配備せずそのまま実行する方法としましたが、Canisterに配置したテストの方法は未調査。
$ cargo test7. Local Canisterの起動
Local Canisterを起動します。
--backgroundオプションでサービス常駐でき、--cleanを付与すると真っ新な状態でLocal canisterを起動できます。
$ dfx start --background --clean8. Local Canisterへの配備
$ dfx deploy最終更新
役に立ちましたか?
