ついに実行の時...!
何も悪いことは言わないので、pnpm tauri dev
と打ってみましょう。
Vite が起動した後、Rust のコンパイルが始まります。
初回の Rust のコンパイルは絶対時間がかかるので、気長に待ちましょう。
気長に(私の環境だと、大体 3 分ぐらい)待ってると...!?
うおおおお!!!!(歓喜!歓喜!)
なんか GUI が出てきたぞ~~!!!!
以上。
もう少しだけ触ってみるか...
Enter a name...
となってるところに名前を入れましょう。
こんな感じですか?
んで、Greet
を押すと...
おお。名前で呼んでくれた~!
これこれ。スタバのモバイルオーダーの時の嬉しさよな。
構造を見てみよう
ここからは VSCode なり、適当なエディターで構造を見ていきます。
エディターでプロジェクトフォルダを見ておきましょう。
沢山フォルダがあると思いますが、2 種類に分けられます。
src
フォルダとindex.html
ファイルがフロントエンドの部分で、src-tauri
フォルダがバックエンドの部分です。
フロントエンド
試しにindex.html
を開いてみて...大事なところはform
タグの中ですね。
<form class="row" id="greet-form">
<input id="greet-input" placeholder="Enter a name..." />
<button type="submit">Greet</button>
</form>
ここが先ほどの名前を入れたら挨拶してくれる部分ですね。
<input>
で名前を受け入れて、<button>
を押したら挨拶してくれる~...という感じですね。
じゃあ実際にどのようにその挨拶が行われているのでしょうか?
フロントエンドとバックエンドの橋渡し!
src
フォルダの中にmain.ts
というファイルがあります。
このファイルが、index.html
を制御している TypeScript ファイルです。
import { invoke } from "@tauri-apps/api/core";
1 行目にこんなものがありますが...
@taur-apps/api/core
の中からinvoke
というのを import しています。
(Python で読み替えたらfrom tauri-apps.api.core import invoke
みたいな感じ...)
invoke
は、バックエンドの Rust の関数を呼び出すための関数です。
第 1 引数に呼び出したい関数名を入れ、第 2 引数には呼び出す関数に渡す引数を入れます。
サンプルで書かれてるコードはこんな感じですね。
let greetInputEl: HTMLInputElement | null;
let greetMsgEl: HTMLElement | null;
async function greet() {
if (greetMsgEl && greetInputEl) {
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
greetMsgEl.textContent = await invoke("greet", {
name: greetInputEl.value,
});
}
}
window.addEventListener("DOMContentLoaded", () => {
greetInputEl = document.querySelector("#greet-input");
greetMsgEl = document.querySelector("#greet-msg");
document.querySelector("#greet-form")?.addEventListener("submit", (e) => {
e.preventDefault();
greet();
});
});
TypeScript や DOM 操作に対する知識がないと、容易には理解できないかもしれませんが、簡単な説明だけしておきますと...
最初のlet (変数名): (型);
は、変数を宣言しています。
TypeScript は Python と異なり、変数宣言時にvar
やlet
、const
を使い、その変数の仕様を明示する必要があります。
ここについては以下の記事が参考になるかと思いますので、興味があれば読んでみてください。
型については、HTMLInputElement
やHTMLElement
は、それぞれ<input>
や<div>
などの HTML 要素を表す型です。
| null
は、その変数がnull
を許容することを示しています。
async function greet()
は、greet
という関数を宣言しています。
この関数が、ボタンを押したときに呼ばれる中身で、invoke
を使ってバックエンドの関数 greet
を呼び出しています。
async
は、非同期関数であることを示しています。
非同期関数については後の方の記事でなんとなく解説します多分...
Python よりは簡単だと思います。
バックエンド
んでさっきからバックエンドバックエンドゆーてるバックエンドってのはなんじゃいっていうお話ですが、src-tauri/src/lib.rs
がバックエンドの部分です。
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
これがさっきのinvoke
で呼び出していたgreet
関数です。
Rust を見たことがない人にとってはなんか難しそうな感じしますが、これも簡単に説明します。
#[tauri::command]
は、マクロと呼ばれるもの一種「手続き型マクロ」というものです。
まずマクロというのが、簡単に言ってしまえばコードを生成するためのものです...多分。
なんとなく C 言語の#define
に似た匂いがします。
特に手続き型マクロは、Python のデコレーターになんとなく似てるかもしれません。
Python で DiscordBOT を作ったことがあるあなたなら「なんとなく@commands.Command
をつければ動く」を知ってるでしょう。それだよそれ。
fn greet(name: &str) -> String
は、greet
という関数を宣言しています。
Rust では、fn (関数名)(引数: 引数の型) -> 戻り値の型
という形式で関数を宣言します。
&str
というものは、文字列スライスと呼ばれているもので、そのまんま文字のスライスです。
String
は、文字列そのものを表す型で、実際には文字の配列だったと思います。
え?何が違うの?スライス?配列?
と思ったかもしれません。
Python 的な考えで言うならスライスも配列も同じだろう多分、と。
配列は、コンパイル時に長さが決まっているもので、スライスは長さが決まっていないものです。
んでまぁ、この関数は&str
を受け取って、String
を返す関数です。
...が、return
みたいなのがありませんね...?
Rust は、C 言語や TypeScript と同じように、関数の最後にはセミコロンをつける必要があります。
が! Rust ではセミコロンをつけなくてもいい場合があります。
それが、関数のreturn
として使用する場合(正確には式
と文
の違い)です。
その値を、その値を内包する関数の戻り値として返したいのあれでば、わざわざreturn
を使わずとも、その値をセミコロン抜きで書けばいいのです。
なんで、こんな関数でも成立します。
fn prime_number() -> i32 {
57
}
この関数は、セミコロンがない 57 が中にあるので、57 を返すだけの関数です。
話を戻しますが、format!
というのは、文字列をフォーマットするマクロです。
うわ!またマクロだ!
Python の f-string とかテンプレートリテラルのように、文字列の中に変数を埋め込むことができるやつです。
ねっ? 簡単でしょ?
まとめ......ない
というわけで実際に動かすことが出来ました...!
暇ならいろいろいじってみてください!
明日からは全てを無に帰させて、新しいものを作ります。
Rustは当分の間出てきません。悲しいね。