rust 编译wasm 使用

创建rust lib 项目

cargo new --lib rust_wasm

修改Cargo.toml

cdylib 是动态库, 可以编译多个跨平台动态库(rlib 是给rust 代码内部用的,例如你写了个算法但是不想暴露算法具体规则只想给别人用,就可以使用rlib 了)

复制代码
[lib]
crate-type = ["cdylib"]

依赖库

复制代码
cargo add wasm-bindge

lib.rs rust 代码

例如 暴露方法就使用 #[wasm_bindgen],具体用法看官网文档

c 复制代码
use byteorder::{BigEndian, ByteOrder};
use serde::{Deserialize, Serialize};
use serde_json::{self, Value};
use std::io::{Cursor, Read};
use wasm_bindgen::prelude::*;
use serde_wasm_bindgen::{to_value, from_value};
use js_sys::Uint8Array;
use web_sys; // 引入 console 模块

#[wasm_bindgen]
pub fn fib(n: u32) -> u32 {
   if n <= 1 {
      n
   } else {
      fib(n - 1) + fib(n - 2)
   }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct DataPacket {
   msg_id: u32,
   protocol: u8,
   body: Value,
}

#[wasm_bindgen]
pub fn pack(msg_id: u32, protocol: u8, body: JsValue) -> Result<Box<[u8]>, JsValue> {
   let body: Value = from_value(body).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let packet = DataPacket { msg_id, protocol, body };

   let json_bytes = serde_json::to_vec(&packet.body).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let json_length = json_bytes.len() as u32;

   let mut buffer = Vec::with_capacity(4 + 4 + 1 + json_bytes.len());

   let mut msg_id_buf = [0u8; 4];
   BigEndian::write_u32(&mut msg_id_buf, packet.msg_id);
   buffer.extend_from_slice(&msg_id_buf);

   let mut length_buf = [0u8; 4];
   BigEndian::write_u32(&mut length_buf, json_length);
   buffer.extend_from_slice(&length_buf);

   buffer.push(packet.protocol);
   buffer.extend_from_slice(&json_bytes);

   Ok(buffer.into_boxed_slice())
}

#[wasm_bindgen]
pub fn unpack(data: &[u8]) -> Result<JsValue, JsValue> {
   let mut cursor = Cursor::new(data);

   let mut msg_id_buf = [0u8; 4];
   cursor.read_exact(&mut msg_id_buf).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let msg_id = BigEndian::read_u32(&msg_id_buf);

   let mut length_buf = [0u8; 4];
   cursor.read_exact(&mut length_buf).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let json_length = BigEndian::read_u32(&length_buf) as usize;

   let mut protocol_buf = [0u8; 1];
   cursor.read_exact(&mut protocol_buf).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let protocol = protocol_buf[0];

   let mut json_bytes = vec![0u8; json_length];
   cursor.read_exact(&mut json_bytes).map_err(|err| JsValue::from_str(&err.to_string()))?;
   let body: Value = serde_json::from_slice(&json_bytes).map_err(|err| JsValue::from_str(&err.to_string()))?;

   let packet = DataPacket { msg_id, protocol, body };
   to_value(&packet).map_err(|err| JsValue::from_str(&err.to_string()))
}


#[wasm_bindgen]
pub fn process_bytes(data: Uint8Array) -> String {
   // 获取 Uint8Array 的长度
   let data_len = data.length() as usize;
   let mut bytes = Vec::with_capacity(data_len);

   // 将 Uint8Array 的内容复制到 Rust 向量中
   unsafe {
      bytes.set_len(data_len);
      data.copy_to(&mut bytes);
   }

   // 将字节数组转换为字符串
   match String::from_utf8(bytes) {
      Ok(s) => s,
      Err(e) => {
         web_sys::console::error_1(&format!("转换字节到字符串失败: {}", e).into());
         String::from("转换失败")
      },
   }
}

介绍两种打包方式一种就是cargo 打包,第二种 wasm-pack 工具打包

1.cargo 打包安装目标平台,可以通过命令查看 rust 覆盖的平台,还是很强的基本覆盖所有平台,命令查看方式 rustup target list

sh 复制代码
rustup target add wasm32-unknown-unknown
rustup show 确认是否安装成功 或者  rustup target list --installed 
cargo build --target wasm32-unknown-unknown
# 生成 target/debug 有.wasm 后缀

为了让js直接调用,由于我们使用了 wasm_bindgen 所以也要安装对应的插件,把js封装让开发人员直接调用

复制代码
cargo install wasm-bindgen-cli
cargo install --list 查看
然后使用命令
wasm-bindgen target/wasm32-unknown-unknown/debug/rust_wasm.wasm --out-dir ./out --target web
这个web 是对应的平台使用方式,out 就是js目录

bundler:编译成给webpack之类的脚手架使用
web:编译成web可直接使用
nodejs:编译成可通过require来加载的node模块
deno:编译成可通过import加载的deno模块
no-modules:跟web类似,但是更旧,且不能使用es模块
  1. 直接使用其wasm-pack 更好,支持ts,更多的其他版本

    cargo install wasm-pack
    wasm-pack build --target web # 这个直接生成了pkg目录

    bundler:编译成给webpack之类的脚手架使用
    web:编译成web可直接使用
    nodejs:编译成可通过require来加载的node模块
    deno:编译成可通过import加载的deno模块
    no-modules:跟web类似,但是更旧,且不能使用es模块

现在演示下使用web 版本的wasm

在项目pkg 同一级创建一个index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>使用Rust和WebAssembly</title>
</head>
<body>
Hello, World!
</body>
<script type="module">
    import init, { pack,unpack } from './pkg/rust_wasm.js';

    const run = async () => {
        await init();  // 指定远程的 .wasm 文件
          const result = fib(10);
        console.log("result:",result);
        // 示例: 调用 pack 函数
        const packet = pack(1, 1, JSON.stringify({ u: 3333 }));
        console.log('Packed data:', packet);

        // 示例: 调用 unpack 函数
        const unpackedData = unpack(packet);
        console.log('Unpacked data:', unpackedData);}

    run();
</script>
</html>

由于wasm 不能是加载本地文件方式,所以需要启动个http服务

复制代码
在.index.html 和pkg 和 index.html 同一级,访问域名看终端输出看终端输出
python3 -m http.server
默认是 8000 端口, 访问 http://127.0.0.1:8000/ ,

如果客户端项目node 和这前端项目支持 webpack 打包的可以打出对应的 wasm

参考链接:
https://github.com/hunter-ji/Blog/issues/72#issue-1756474747

https://juejin.cn/post/7156102433581383716

相关推荐
peterfei3 小时前
IfAI v0.2.8 技术深度解析:从"工具"到"平台"的架构演进
rust·ai编程
栈与堆7 小时前
LeetCode-1-两数之和
java·数据结构·后端·python·算法·leetcode·rust
superman超哥7 小时前
双端迭代器(DoubleEndedIterator):Rust双向遍历的优雅实现
开发语言·后端·rust·双端迭代器·rust双向遍历
福大大架构师每日一题8 小时前
2026年1月TIOBE编程语言排行榜,Go语言排名第16,Rust语言排名13。C# 当选 2025 年度编程语言。
golang·rust·c#
superman超哥10 小时前
精确大小迭代器(ExactSizeIterator):Rust性能优化的隐藏利器
开发语言·后端·rust·编程语言·rust性能优化·精确大小迭代器
superman超哥11 小时前
惰性求值(Lazy Evaluation)机制:Rust 中的优雅与高效
开发语言·后端·rust·编程语言·lazy evaluation·rust惰性求值
古城小栈11 小时前
Rust IO 操作 一文全解析
开发语言·rust
superman超哥11 小时前
迭代器适配器(map、filter、fold等):Rust函数式编程的艺术
开发语言·rust·编程语言·rust map·rust filter·rust fold·rust函数式
superman超哥12 小时前
Iterator Trait 的核心方法:深入理解与实践
开发语言·后端·rust·iterator trait·trait核心方法
superman超哥12 小时前
自定义迭代器的实现方法:深入Rust迭代器机制的核心
开发语言·后端·rust·编程语言·rust迭代器机制·自定义迭代器