Rust 型別轉換

先前我們大多看到的是當值進入 Rust 時,型別轉換的簡化版本。在這裡,我們將更深入地探討這是如何實現的。轉換值有兩個類別的 trait,一個是將值從 Rust 轉換為 JS 的 trait,另一個是反向轉換的 trait。

從 Rust 到 JS

首先,讓我們看看從 Rust 到 JS 的轉換


# #![allow(unused_variables)]
#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_variables)]
    #fn main() {
    pub struct WasmSlice {
        pub ptr: u32,
        pub len: u32,
    }
    #}

    這個結構體 (這是字串之類的東西在 FFI 中表示的方式) 並不是 WebAssembly 原始型別,因此無法直接對應到 WebAssembly 參數/傳回值。這就是為什麼 WasmAbi 允許型別指定如何將其拆分為多個 WebAssembly 參數

    
    # #![allow(unused_variables)]
    #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 函式,傳回 Abi 關聯型別,這將實際傳遞給 JS。

這個 trait 是針對所有可以轉換為 JS 的型別實作的,並且在程式碼產生期間無條件使用。例如,您經常會看到 IntoWasmAbi for Foo,也會看到 IntoWasmAbi for &'a Foo

IntoWasmAbi trait 用於兩個位置。首先,它用於將 Rust 匯出函式的傳回值轉換為 JS。其次,它用於轉換匯入到 Rust 的 JS 函式的 Rust 引數。

從 JS 到 Rust

不幸的是,與上述相反的方向 (從 JS 到 Rust) 會稍微複雜一些。這裡我們有三個 trait


# #![allow(unused_variables)]
#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 相同,除了它們傳回實作了 Deref trait 而非 SelfAnchor 型別。

Ref* trait 允許在函式中使用引數 (它們是參考,而不是裸型別),例如 &str&JsValue&[u8]。這裡需要 Anchor 來確保生命週期不會持續超過一個函式呼叫並且保持匿名。

From* 系列的 trait 用於將 Rust 導出函數中的 Rust 參數轉換為 JavaScript 參數。它們也用於導入到 Rust 的 JavaScript 函數中的回傳值。