Rust 型別轉換

先前我們看到的主要是當值進入 Rust 時,型別轉換的簡略版本。在這裡,我們將深入探討這是如何實作的。轉換值的特性有兩種類別,一類是將值從 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 值所需的唯一特性。這裡有幾點需要說明

  • 我們將在本節稍後介紹 WasmDescribe

  • 關聯型別 Abi 是我們實際想要傳遞給 JS 的原始資料的型別。對於像 u32f64 這樣的基本型別,以及其他一些型別,例如 WasmSlice,已經實作了界限 WasmAbi,這些型別可以直接表示為 WebAssembly 值。

    #![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 關聯型別。

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

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

從 JS 到 Rust

不幸的是,與上面相反的方向,從 JS 到 Rust 的轉換稍微複雜一些。這裡我們有三個特性

#![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 的實例。此特性主要針對 _沒有_ 內部生命週期或為參考的型別實作。

這裡的後兩個特性大多相同,旨在產生參考 (共享參考和可變參考)。它們看起來與 FromWasmAbi 幾乎相同,只是它們傳回一個實作 Deref 特性而不是 SelfAnchor 型別。

Ref* 特性允許在函式中包含參考而不是裸型別的引數,例如 &str&JsValue&[u8]。此處需要 Anchor 以確保生命週期不會超過一個函式呼叫並且保持匿名。

From* 系列特性用於將 Rust 匯出函式中的 Rust 引數轉換為 JS。它們也用於匯入 Rust 的 JS 函式的傳回值。