Rust 类型转换

之前我们主要看到了值进入 Rust 时类型转换的简略版本。在这里,我们将更深入地了解其实现方式。有两种类型的 trait 用于转换值,一种用于将值从 Rust 转换为 JS,另一种用于反向转换。

从 Rust 到 JS

首先,让我们看看从 Rust 到 JS 的转换

#![allow(unused)]
fn main() {
pub trait IntoWasmAbi: WasmDescribe {
    type Abi: WasmAbi;
    fn into_abi(self) -> Self::Abi;
}
}

就是这样!实际上,这是目前将 Rust 值转换为 JS 值所需的唯一 trait。这里有一些要点

  • 我们将在本节后面介绍 WasmDescribe

  • 关联类型 Abi 是我们实际要传递给 JS 的原始数据的类型。绑定 WasmAbi 是为像 u32f64 这样的基本类型实现的,它们可以被直接表示为 WebAssembly 值,以及其他一些类型,比如 WasmSlice

    #![allow(unused)]
    fn main() {
    pub struct WasmSlice {
        pub ptr: u32,
        pub len: u32,
    }
    }

    这个结构体是像字符串这样的东西在 FFI 中的表示方式,它不是 WebAssembly 基本类型,因此不能直接映射到 WebAssembly 参数/返回值。这就是为什么 WasmAbi 允许类型指定如何将它们拆分为多个 WebAssembly 参数

    #![allow(unused)]
    fn main() {
    impl WasmAbi for WasmSlice {
        fn split(self) -> (u32, u32, (), ()) {
            (self.ptr, self.len, (), ())
        }
    
        // some other details to specify return type of `split`, go in the other direction
    }
    }

    这意味着 WasmSlice 被拆分为两个 u32 参数。末尾的额外单元类型是存在的,因为 Rust 不允许我们对可变长度元组进行 WasmAbi 通用化,所以我们只接受 4 个元素的元组。单元类型最终仍然会被传递到/从 JS,但 C ABI 只是完全忽略它们,并且不会生成任何参数。

    由于我们不能返回多个值,因此在返回 WasmSlice 时,我们将两个 u32 放入一个 #[repr(C)] 结构体中并返回它。

  • 最后,我们有 into_abi 函数,它返回将实际传递给 JS 的关联类型 Abi

这个 trait 是为所有可以转换为 JS 的类型实现的,并且在代码生成期间被无条件地使用。例如,你经常会看到 IntoWasmAbi for Foo,但也会看到 IntoWasmAbi for &'a Foo

IntoWasmAbi trait 在两个地方使用。首先,它用于将 Rust 导出函数的返回值转换为 JS。其次,它用于将 JS 函数导入 Rust 时 Rust 参数转换为 JS。

从 JS 到 Rust

不幸的是,与上面的相反方向,从 JS 到 Rust 的转换要复杂一些。这里我们有三个 trait

#![allow(unused)]
fn main() {
pub trait FromWasmAbi: WasmDescribe {
    type Abi: WasmAbi;
    unsafe fn from_abi(js: Self::Abi) -> Self;
}

pub trait RefFromWasmAbi: WasmDescribe {
    type Abi: WasmAbi;
    type Anchor: Deref<Target=Self>;
    unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor;
}

pub trait RefMutFromWasmAbi: WasmDescribe {
    type Abi: WasmAbi;
    type Anchor: DerefMut<Target=Self>;
    unsafe fn ref_mut_from_abi(js: Self::Abi) -> Self::Anchor;
}
}

FromWasmAbi 相对简单,基本上是 IntoWasmAbi 的反面。它接受 ABI 参数(通常与 IntoWasmAbi::Abi 相同)来生成 Self 的实例。这个 trait 主要为没有内部生命周期或不是引用的类型实现。

后面的两个 trait 基本上是相同的,并且用于生成引用(共享引用和可变引用)。它们看起来与 FromWasmAbi 几乎相同,只是它们返回一个 Anchor 类型,该类型实现了 Deref trait 而不是 Self

Ref* trait 允许在函数中使用引用作为参数,而不是裸类型,例如 &str&JsValue&[u8]。这里的 Anchor 是必需的,以确保生命周期不会超过一次函数调用,并且保持匿名。

From* 特性族用于将 Rust 导出函数中的 Rust 参数转换为 JS。它们也用于将 JS 函数导入 Rust 时返回的值。