【Rust】Tauriでフロントからバックエンドの処理を呼び出す

Linux,Rust

こんにちは、しきゆらです。
前回投稿したTauriで、触れたのがフロントエンド部分のみだったので 今回はバックエンドであるRustの処理をどのように呼び出すかをサンプルコードから追っていきます。

前回の記事はこちら。

公式ページはこの辺を参考。


TauriのフロントエンドからRustのコードを呼び出す

サンプルコードを確認

サンプルコードのフロントエンドはこんな感じでした。

import { useState } from "react";
import reactLogo from "./assets/react.svg";
import { invoke } from "@tauri-apps/api/tauri";
import "./App.css";

function App() {
  const [greetMsg, setGreetMsg] = useState("");
  const [name, setName] = useState("");

  async function greet() {
    // Learn more about Tauri commands at <https://tauri.app/v1/guides/features/command>
    setGreetMsg(await invoke("greet", { name }));
  }

  return (
    <div className="container">
      <h1>Welcome to Tauri!</h1>

      <div className="row">
        <a href="<https://vitejs.dev>" target="_blank">
          <img src="/vite.svg" className="logo vite" alt="Vite logo" />
        </a>
        <a href="<https://tauri.app>" target="_blank">
          <img src="/tauri.svg" className="logo tauri" alt="Tauri logo" />
        </a>
        <a href="<https://reactjs.org>" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>

      <p>Click on the Tauri, Vite, and React logos to learn more.</p>

      <form
        className="row"
        onSubmit={(e) => {
          e.preventDefault();
          greet();
        }}
      >
        <input
          id="greet-input"
          onChange={(e) => setName(e.currentTarget.value)}
          placeholder="Enter a name..."
        />
        <button type="submit">Greet</button>
      </form>

      <p>{greetMsg}</p>
    </div>
  );
}

export default App;

Reactを使っているのでわかりにくいかもしれませんが、基本的には普通のJSコードです。

バックエンド側のRustのコードはこんな感じ。

// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

// Learn more about Tauri commands at <https://tauri.app/v1/guides/features/command>
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

主にこの2つのコードで、Tauriのサンプルであるフロント表示とユーザ入力に対する応答を記述しています。

フロントからRustを呼び出している部分

では、実際にフロントからRustのコードを呼び出している部分を抜き出してみます。
まずは、呼び出し側のJSのほうはこの部分。

async function greet() {
  // Learn more about Tauri commands at <https://tauri.app/v1/guides/features/command>
  setGreetMsg(await invoke("greet", { name }));
}

1つの関数ですが、やっていることはinvoke("greet", { name })で名前を受け取って文字列を挨拶文を作成し、その結果をsetGreetMsgでReactの状態として更新している感じ。

では、invoke関数は何者か。
ここがRust側のコードを呼び出している部分になっています。

invoke関数でRust側を呼ぶとPromiseが返ってくるのでasync/awaitthen/catchを使って処理を記述します。

フロントから呼ばれるRustの部分

Rust側の呼ばれている関数や準備部分はこの部分。 呼ばれる関数はgreetの部分、.invoke_handler(tauri::generate_handler![greet])の部分でフロントから呼び出せる関数を列挙している感じ。

#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

// main関数の中のここで、外部から叩ける関数を列挙する
tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])

処理の流れを追っていくと、まずはフロント側Greetボタンを押すとJS内のgreet関数を呼びだす。

JSのgreet関数は、input要素の入力を受け取ってRustへ渡し、返り値をReactの状態として上書きしています。

Rustのgreet関数は、文字列を受け取りHello, <受け取った文字列>! You've been greeted from Rust!という文字列を生成して返しています。

それぞれの関数でやっていることはシンプルなので、Tauriを使ったフロントとRustのやり取りがわかりやすい。

なお、今回はフロントであるJSから渡している引数もRust側で受け取っている引数もnameなので、invoke関数での引数は{ name }となっていますが、基本的には{ key: value }の形式で指定することになります。 この辺は、キーと変数名が同じであれば省略して書くことができるので引数のみになっています。

オブジェクト初期化子 – JavaScript | MDN

引数の注意点

この引数の注意点としては、公式サイトにも記載がありますが JS側ではキャメルケースでキーを指定すると、Rust側ではスネークケースに返還される、ということです。

Your command handlers can take arguments:

#[tauri::command]
fn my_custom_command(invoke_message: String) {
  println!("I was invoked from JS, with this message: {}", invoke_message);
}

Arguments should be passed as a JSON object with camelCase keys:

invoke('my_custom_command', { invokeMessage: 'Hello!' })

Arguments can be of any type, as long as they implement [serde::Deserialize](<https://docs.serde.rs/serde/trait.Deserialize.html>).

Please note, when declaring arguments in Rust using snake_case, the arguments are converted to camelCase for JavaScript.

https://tauri.app/v1/guides/features/command/#passing-arguments

これは、JSは基本的にキャメルケース、Rustではスネークケースで記載するからかなと。

もし、JS側からスネークケースで渡したい場合は、Rust側で教えてあげる必要があります。

To use snake_case in JavaScript, you have to declare it in the tauri::command statement:

#[tauri::command(rename_all = "snake_case")]
fn my_custom_command(invoke_message: String) {
  println!("I was invoked from JS, with this message: {}", invoke_message);
}
https://tauri.app/v1/guides/features/command/#passing-arguments

まとめ

今回は、Tauriを使ったデスクトップアプリを作る中で出てくるであろう、フロント側からバックエンドであるRustを呼ぶ場合の方法をサンプルコードをもとに追っていきました。

個人的にはElectronよりもわかりやすく、とっつきやすい印象を受けました。 もう少しフロントとRustでやり取りする必要があるコードを書いてみたいです。

手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた

クジラ飛行机
3,799円(04/29 22:13時点)
発売日: 2022/01/20
Amazonの情報を掲載しています

今回は、ここまで。

おわり

Posted by しきゆら