icon

nazo6.dev

一覧に戻る
2022/2/13 6 min read

Rustアプリにwasmerを埋め込む


#(2022-02-13)

  • dioxusを使ってwebでもdesktopでも動くアプリを作りたい
  • プラグインシステムを作りたい
  • けどプラグインもRustで書きたい
  • → wasm
  • wasmerにはwasmer-jsというものがあってどうやらwasm環境でも動かせる?

#環境 (2022-02-13)

  • arch on wsl
  • Rustはなるべくnightlyを使わないようにしたい

#プロジェクト作成 (2022-02-13)

構成はこんな感じにする

app
│ public
│ src
│ └ main.rs
│ Cargo.toml
└ Dioxus.toml
plugins
└ sample
  │ src
  │ └ lib.rs
  └ Cargo.toml
Cargo.toml

(どうでもいいけどこういうときvimだとファイラーのテキスト直接コピーできて便利だな)

app以下はdioxus-cliで作る

$ dioxus create

#(2022-02-13)

ワークスペースのルートCargo.toml

root/Cargo.toml
[workspace]
resolver = "2"
members = ["app", "plugins/sample"]

ここでresolver = "2"を指定するのが重要。こうしないとcfgでfeature出し分けができない(超ハマった)

#(2022-02-13)

appのCargo.toml

root/app/Cargo.toml
[package]
name = "<app name>"
version = "0.1.0"
edition = "2021"
 
[target.'cfg(target_arch = "wasm32")'.dependencies]
dioxus = { version = "0.1.8", features = ["web"] }
wasmer = { version = "2.0", features = [
  "js-default",
], default-features = false }
wasm-logger = "0.2.0"
console_error_panic_hook = "0.1.7"
 
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
dioxus = { version = "0.1.8", features = ["desktop"] }
wasmer = { version = "2.0" }

これでビルド時に切り替えられる

参考: https://docs.rs/crate/wasmer/2.2.0-rc1

#プラグイン側wasmを作る (2022-02-13)

とりあえずこの記事を参考にして作る @gist unsafeだけどひとまず我慢

#(2022-02-13)

あれ?ワークスペースごとにターゲットの設定できるんだっけと思ったら案の定nightly限定機能みたいなのでしょうがないのでnightlyにする

https://github.com/rust-lang/cargo/issues/7004 https://doc.rust-lang.org/cargo/reference/unstable.html#per-package-target

workspace/rust-toolchain.toml
[toolchain]
channel = "nightly"
targets = ["wasm32-unknown-unknown"]
workspace/plugins/sample/Cargo.toml
cargo-features = ["per-package-target"]
 
[package]
name = "plugin-sample"
version = "0.1.0"
edition = "2021"
default-target = "wasm32-unknown-unknown"
[dependencies]
 
[lib]
crate-type = ["cdylib"]

#(2022-02-13)

とりあえずプラグイン側だけビルドしてwatを見てみる

$ cargo build -p plugin-sample
## wasm2watコマンドを入れる
$ wasm2wat ./target/wasm32-unknown-unknown/debug/plugin_sample.wasm > plugin_sample.wat

結果

plugin_sample.wat
(module
  (type (;0;) (func (param i32 i32)))
  (type (;1;) (func (param i32 i32) (result i32)))
  (type (;2;) (func))
  (import "env" "print_str" (func $print_str (type 0)))
  (func $_ZN4core3str21_$LT$impl$u20$str$GT$3len17hd6f34eff50e5c25bE (type 1) (param i32 i32) (result i32)
    (local i32 i32 i32 i32)
    global.get 0
    local.set 2
    i32.const 32
    local.set 3
    local.get 2
    local.get 3
    i32.sub
    local.set 4
    local.get 4
    local.get 0
    i32.store offset=8
    local.get 4
    local.get 1
    i32.store offset=12
    local.get 4
    local.get 0
    i32.store offset=16
    local.get 4
    local.get 1
    i32.store offset=20
    local.get 4
    local.get 0
    i32.store offset=24
    local.get 4
    local.get 1
    i32.store offset=28
    local.get 4
    i32.load offset=24
    drop
    local.get 4
    i32.load offset=28
    local.set 5
    local.get 5
    return)
  (func $_ZN4core3str21_$LT$impl$u20$str$GT$6as_ptr17hc3437732d3c877b4E (type 1) (param i32 i32) (result i32)
    (local i32 i32 i32)
    global.get 0
    local.set 2
    i32.const 16
    local.set 3
    local.get 2
    local.get 3
    i32.sub
    local.set 4
    local.get 4
    local.get 0
    i32.store offset=8
    local.get 4
    local.get 1
    i32.store offset=12
    local.get 0
    return)
  (func $hello_wasm (type 2)
    (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32)
    i32.const 0
    local.set 0
    local.get 0
    i32.load offset=1048592
    local.set 1
    i32.const 0
    local.set 2
    local.get 2
    i32.load offset=1048596
    local.set 3
    local.get 1
    local.get 3
    call $_ZN4core3str21_$LT$impl$u20$str$GT$6as_ptr17hc3437732d3c877b4E
    local.set 4
    i32.const 0
    local.set 5
    local.get 5
    i32.load offset=1048592
    local.set 6
    i32.const 0
    local.set 7
    local.get 7
    i32.load offset=1048596
    local.set 8
    local.get 6
    local.get 8
    call $_ZN4core3str21_$LT$impl$u20$str$GT$3len17hd6f34eff50e5c25bE
    local.set 9
    local.get 4
    local.get 9
    call $print_str
    return)
  (table (;0;) 1 1 funcref)
  (memory (;0;) 17)
  (global (;0;) (mut i32) (i32.const 1048576))
  (global (;1;) i32 (i32.const 1048600))
  (global (;2;) i32 (i32.const 1048608))
  (export "memory" (memory 0))
  (export "hello_wasm" (func $hello_wasm))
  (export "__data_end" (global 1))
  (export "__heap_base" (global 2))
  (data (;0;) (i32.const 1048576) "Hello, World!\00\00\00\00\00\10\00\0d\00\00\00"))

普通のアセンブリは全然わかんないけどwatは割と見ただけでわかる感じ

#(2022-02-13)

よくわからないけどdioxusはスレッドセーフではなくてwasmからstateにアクセスできない? Rust初心者すぎて何もわからないのでとりあえずdioxusとの連携はなかったことにする

#(2022-02-13)

数時間の格闘の末についにHello Worldの出力に成功した wasmerは昔の情報しかなくてつらみがあった

$ cargo build -p plugin-sample
$ cargo run
workspace/app/src/main.rs
use dioxus::prelude::*;
 
use wasmer::*;
 
fn main() {
    launch();
}
 
#[derive(WasmerEnv, Clone, Default)]
pub struct Env {
    #[wasmer(export)]
    memory: LazyInit<Memory>,
}
 
fn call_wasm() {
    static wasm_bytes: &'static [u8] =
        include_bytes!("../../target/wasm32-unknown-unknown/debug/plugin_sample.wasm");
    let store = Store::default();
    let module = Module::new(&store, wasm_bytes).unwrap();
 
    fn print_str(env: &Env, ptr: u32, length: u32) {
        println!("ptr:{}, length:{}", ptr, length);
        let wasmptr: WasmPtr<u8, Array> = WasmPtr::new(ptr);
        let str = wasmptr
            .get_utf8_string(env.memory_ref().unwrap(), length)
            .unwrap();
        println!("{}", str);
    }
 
    let print_str_func = Function::new_native_with_env(&store, Env::default(), print_str);
    let import_object = imports! {
        "env" => {
             "print_str" => print_str_func,
        }
    };
    let instance = Instance::new(&module, &import_object).unwrap();
    let hello = instance
        .exports
        .get_function("hello_wasm")
        .unwrap()
        .native::<(), ()>()
        .unwrap();
    hello.call();
}
 
#[cfg(target_arch = "wasm32")]
fn luanch() {
    wasm_logger::init(wasm_logger::Config::default());
    console_error_panic_hook::set_once();
 
    dioxus::web::launch(app);
}
#[cfg(not(target_arch = "wasm32"))]
fn launch() {
    dioxus::desktop::launch_cfg(app, |c| {
        c.with_window(|w| {
            w.with_resizable(true).with_inner_size(
                dioxus::desktop::wry::application::dpi::LogicalSize::new(400.0, 800.0),
            )
        })
    });
}
 
fn app(cx: Scope) -> Element {
    let (text, set_text) = use_state(&cx, || "");
 
    cx.render(rsx!(
        button {onclick: |_|{call_wasm();} , "print" }
        p { "{text}" }
    ))
}

#(2022-02-13)

こんな感じ

#(2022-02-13)

https://github.com/zellij-org/zellij/blob/main/zellij-server/src/wasm_vm.rs

これを見るとzellijではメモリ操作みたいな面倒くさいことをせずにwasiの標準入出力でデータをやり取りしているみたい こっちのほうが大分楽そう

Share this article:
一覧に戻る

関連記事

2021/12/27

2023/10/20

#tech/lang/rust
memo

Rust

気づきとかいろいろ

Read Article

2024/3/23

#tech/lang/rust#hardware/keyboard/keyball
blog

RustでKeyballのファームウェアを書きたい話

KeyballのファームウェアはQMKを使ったC言語のものになっています。ですがやはりRust、使いたいですよね?

Read Article

2023/5/26

#tech/lang/rust#tech/database
memo

RustでSQLからコードを生成するcornucopiaについて

SQLからRustのコードを生成して安全にデータベース操作ができる。恐らくGoのsqlcと同じ感じなんだと思う。

Read Article

2023/11/15

#tech/lang/rust
memo

Rustでジョブキュー的なもの

実行するコマンド(EnqueueかClear)をチャネルで受け取る

Read Article

2025/12/13

2025/12/14

#tech/lang/rust
blog

RustでデスクトップGUI - gpui入門 Part1 (gpuiの仕組み・状態管理の基礎編)

gpui解説記事のPart1。gpuiのレンダリング方法や状態管理について、実際のソースを見ながら詳しく解説します。

Read Article

2024/4/26

2024/5/22

#tech/lang/rust#hardware/keyboard/keyball
blog

RustとEmbassyでKeyballのファームウェアを作った

以前RustでKeyballのファームウェアを書きたい話で、ATMega32U4向けのファームウェアの作成をRustで試みたという話を書きましたが、結論から言うとこれは諦めてProMicro RP2040向けのファームウェアをRustで書くことにしました。

Read Article

2023/8/27

#tech/lang/rust
memo

Rustのserde_jsonでエラーの発生箇所を知る方法

serde_jsonではパースエラー発生時にどのプロパティでエラーが発生したのかわからない

Read Article

2023/6/27

#tech/lang/rust
memo

Rustのtargetフォルダを軽くする

cargo-sweepを使う

Read Article

2025/3/29

#tech/lang/rust
memo

Rustアプリのメモリ使用量を調査する

主にstatic領域のメモリ使用量を調査するのに有益。embassyの独立したタスクなどのサイズが見れる。

Read Article

2023/9/10

2023/12/18

#tech/lang/rust
blog

SerdeのDeserializerを実装する(Part1)

Serdeで任意の形式のファイルなどをデシリアライズする際にはDeserializerを書く必要があります。この記事では基本的なDeserializerの書き方を解説します。 正直自分もあまり理解していない部分が多々あるのですが世に出ている情報が少ないので書くことにしました。

Read Article

2023/12/18

2023/12/19

#tech/lang/rust
blog

SerdeのDeserializerを実装する(Part2 JSON編)

この記事はRust Advent Calendar 2023 シリーズ3の19日目の記事です。

Read Article

2024/5/24

#hardware/keyboard#tech/lang/rust
blog

USB HIDキーボードでメディアキーを操作する方法

USB HIDでは0x80がVolume Up、0x81がVolume Downに割り当てられており、さらに0xEDや0xEEでもVolume UpやDownができそうですが、実はこれらは全て動きません(Windowsでは)。

Read Article

2023/9/1

#tech/lang/rust
blog

prisma-client-rust入門

prisma-client-rustはJavascript向けのORMであるprismaをRustから使えるようにしたものです。実はprismaのコア部分はRustで書かれているためこういうものも作りやすかったんじゃないかと思います。

Read Article

2021/12/25

#tech/lang/rust
memo

tauriでWindows上でproductionビルドでのみ画像が表示されない(fetchエラーが発生する)

誰の役にも立たない気がするけどハマったのでメモ

Read Article

2023/11/18

#tech/lang/rust
blog

tokioで作ったサーバーをdockerで起動すると終了が遅くなるときの対処法

axumなどを作ってRustでサーバーを作るとdocker compose stopなどが微妙に遅くてイライラだったのでそれを解決する方法です。

Read Article

2025/4/9

2025/11/5

#tech/lang/rust
blog

「Rustが嫌いです。」の感想

https://zenn.dev/miguel/articles/f052de93fc9980

Read Article

© 2025 nazo6. All rights reserved.