wasm-bindgen 开发指南 (wasmdev.cn) - 官方文档
Rust 不是面向对象的编程语言,没有类的概念(Class),但是它具有一些面向对象的特性
它有结构体 Struct 和枚举 Enum ,通过结构体和枚举等类型,可以用来封装属性数据
结构体本身,不能直接定义函数,但可以在结构体的实现中(impl 块),为结构体定义方法
枚举成员本身,不能直接添加方法,但可以通过关联函数来达到添加函数的效果(impl 块)
目录:
一、Rust 方法格式
二、Rust 结构体
1、定义结构体
2、结构体实例化
3、定义结构体的方法
4、输出结构体
三、Rust 枚举类
1、定义枚举类
2、实例化枚举类
3、定义枚举类的方法
四、trait 特征对象
五、编写 rust 和 JS 代码(关键)
1、必备的结构
2、结构体参数
3、枚举类参数
4、元组参数
5、数组/切片参数
6、混合参数
一、Rust 方法格式
impl 结构体名 | 枚举名 | 特征名 { // Rust 使用 impl
关键字定义方法
函数1
函数2 ...
}
二、Rust 结构体
Rust 的结构体(Struct)与元组(Tuple)都可以将若干个类型不一定相同的数据捆绑在一起形成整体,但结构体的每个成员和其本身都有一个名字,访问成员时,不用下标,直接用名字即可。
1、定义结构体
struct Site { // 结尾无分号,每个字段定义后逗号分隔(区别于C/C++)
domain: String,
name: String,
nation: String,
width: u32,
height: u32,
}
2、结构体实例化
Rust 很多地方受 JS 的影响,在实例化结构体时,经常采用 JSON 对象的 key: value 语法格式
let runoob = Site {
domain: String::from( "www.runoob.com" ),
name: String::from( "RUNOOB" ),
nation: String::from( "China" ),
width: 30,
eight: 50
};
3、定义结构体的方法
Rust 使用 impl
关键字定义方法,方法的第一个参数必须是 &self,但在调用时,无需填写 self
impl Site { // 同名
fn distance(&self) -> f64 { // &self :
引用当前结构体的实例
(self.x * self.x + self.y * self.y) as f64 // 如需类型转换,则需明确写出目标类型
}
}
注:&self 是 self: &Self 的简写,作用是通过 self 访问 当前结构体的实例
4、输出结构体
Rust 提供了一种输出整个结构体实例的方法,这在调试中,是非常有用的
#[derive(Debug)] // 引用调试库,放在代码第一行
...... println!("rect1 is {:?} ", rect1); // 用 {:?} 占位符输出一整个结构体
三、Rust 枚举类
枚举的目的是对某一类事物的分类,分类的目的是为了对不同的情况进行描述。
1、定义枚举类
无需描述:enum Book{ Papery, Electronic, }
类型描述:enum Book { Papery(u32), Electronic(String), }
属性命名:enum Book { Papery{ index: u32 }, Electronic{ url: String }, }
2、实例化枚举类
let book = Book::Papery{ index: 1001 };
let ebook = Book::Electronic{ url: String::from("url...") };
注1:创建一个 Book 枚举类型的实例 book, 为 Papery 变体,并设置索引为 1001
注2:创建一个 Book 枚举类型的实例 ebook,为 Electronic 变体,并设置 URL 为指定的字符串
3、定义枚举类的方法
枚举是为了对某一类事物的进行分类,再根据事物所属的枚举值,去执行相应的操作
impl Book {
fn description(&self) -> &'static str { // 访问成员,返回一个静态不可变字符串
match self { // 辨别成员,使用 match 分支判断语句
Book::Papery => "这是纸质书",
Book::Electronic => "这是电子书",
}
}
}
四、trait 特征对象
在 Rust 中,trait
类似接口,它定义了一组方法的签名(名称、参数、返回类型),而不提供具体的实现。任何类型(如结构体、枚举等)都能通过 impl
块来实现 trait
中定义的方法。
trait Draw { // 定义特征 Draw,其中包含 draw
方法的签名
fn draw(&self);
}
struct Circle;
impl Draw for Circle { // 为结构体实现了该特征,拥有了其中的方法
fn draw(&self) {
println!("Drawing a circle");
}
}
struct Square;
impl Draw for Square { // 同上
fn draw(&self) {
println!("Drawing a square");
}
}
fn draw_shape(shape: &dyn Draw) { // 为关联函数实现了该特征,拥有了其中的方法
shape.draw();
}
注:关联函数:指的是定义在 impl 中,且没有 self 的函数。它没有 self,即不能用 obj.method 的形式调用,而是使用::
的形式来调用。因此它是函数不是方法。
五、编写 rust 和 JS 代码(关键)
编写代码的过程中,有三个难点:
1、写出一个正确可运行的 rust 代码
2、根据 wasm 语法规则,对 rust 代码进行改写,将 rust 的相关内容暴漏给 JavaScript
3、写出一个可以调用 wasam 的 JavaScript 代码
注:wasm-bindgen 有其特定的语法规范,如果不遵循其规则,就很可能会导致 rust 的编译失败。比较麻烦的是,明明按照 wasm-bindgen 语法写了,但还是报错,定位错误的时候很麻烦。
1、必备的结构
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
****pub rust函数代码****
注1:第一句,意思是引入 wasm_bindgen
库(一种 Rust 库)的“预备”(prelude)模块,该模块包含一些最常用的函数、宏或类型。当开发者要用某些函数时,就可以从这个模块中直接引用。
注2:第二句,它是一种属性,主要用于标记: ① 标记Rust 导出给 JavaScript 的函数、结构体、枚举、变量等内容;② 标记从 JavaScript 导入到 Rust 中的函数和类。标记完成后 wasm-bindgen
工具,就会根据标记和相关设置,生成相应的 JavaScript 绑定代码。
注3:pub
关键字,主要用于搭配上述属性。因为 Rust 中的内容默认都是私有的,外部无法调用。
2、结构体参数
use wasm_bindgen::prelude::*; // 按照 wasm 语法,改写 Rust 代码
#[wasm_bindgen]
#[derive(Debug)]
pub struct Square { // 定义结构体和相关字段
pub width: u32,
pub height: u32,
}
#[wasm_bindgen]
pub impl Square { // 定义结构体的方法
pub fn new(width: u32, height: u32) -> Self { // 构造函数,被 JS 调用后,创建实例
Self { width, height }
}
pub fn area(&self) -> u32 { // 功能函数,操作实例
self.width * self.height
}
}
注1:new 不是关键字,new()方法,用于创建 Square 对象的构造函数,标记为 pub ,方便后续 JavaScript 调用,使用 Self 关键字 引用当前的类型,创建一个新的结构体实例(看注2)
注2:在 JavaScript 代码中,使用 const square01 = Square.new(5, 10),即创建一个 Square 实例,并将其存储在 square01 变量中。
注3:area()方法,用于实现特定的功能,标记为 pub ,方便后续 JavaScript 的实例对象去调用
3、枚举类参数
由于 JavaScript 原生不支持枚举类型,无法处理 Rust 枚举类型。虽然 wasm_bindgen
能实现 Rust 类型到 JS 类型的转换,但对于枚举类型,它通常不能直接映射,但它们也不会阻止导出。
解决办法: 法一:用整数或字符串代替枚举 法二:只导出与枚举相关的函数
下面的实例,两种方法都用到
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[derive(Copy, Clone, Debug)]
pub enum Season { // 定义枚举类型
Spring = 1, // 为每个枚举值,关联一个整数编码,让 JS 理解
Summer = 2,
Autumn = 3,
Winter = 4,
}
#[wasm_bindgen] // 不去定义枚举类的方法,而是定义一个普通的方法
pub fn get_season_description(season_id: i32) -> String { // 编码值作为参数
match season_id { // match
分支判断
1 => "It's spring, time for new beginnings!".to_string(),
2 => "It's summer, enjoy the warmth and sunshine!".to_string(),
3 => "It's autumn, witness the beauty of changing leaves!".to_string(),
4 => "It's winter, embrace the cold and snow!".to_string(),
_ => "Unknown season".to_string(), // 用于 match
语句中的默认情况
}
}
4、元组参数
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn my_function( tuple: (i32, String )){ // 元组,作为函数参数,供 JavaScript 调用
let (num, str) = tuple; // 解构
println!("Number: {}", num);
println!("String: {}", str);
}
5、数组/切片参数
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn process_fixed_array(arr: [i32 ; 5]) { // 数组,作为函数参数,供 JavaScript 调用
for num in arr {
println!("{}", num);
}
}
pub fn process_slice(arr: &[i32]) { // 数组切片,作为函数参数,供 JavaScript 调用
for num in arr {
println!("{}", num);
}
}
6、混合参数
实现一个函数,同时接受以上所有类型的参数,再进行对应的操作(直接混合即可)。