Đi nào.
Tạo các loại TypeScript cho các mô-đun WebAssembly(Wasm) trong Rust không đơn giản.
Tôi gặp sự cố khi đang làm việc trên một công cụ tìm kiếm độ tương tự véc tơ trong Wasm có tên là Voy . Tôi đã xây dựng công cụ Wasm trong Rust để cung cấp cho các kỹ sư JavaScript và TypeScript một con dao Thụy Sĩ để tìm kiếm ngữ nghĩa. Đây là một bản demo cho web:
Bạn có thể tìm thấy kho lưu trữ của Voy trên GitHub ! Cảm thấy thật thanh thản khi từ bỏ.
Kho lưu trữ bao gồm các ví dụ mà bạn có thể xem cách sử dụng Voy trong các khuôn khổ khác nhau.
Tôi đã sử dụng wasm-pack và wasm-bindgen để xây dựng và biên dịch mã Rust thành Wasm. Các định nghĩa TypeScript được tạo trông như thế này:
/* tslint:disable */ /* eslint-disable */ /** * @param {any} input * @returns {string} */ export function index(resource: any): string /** * @param {string} index * @param {any} query * @param {number} k * @returns {any} */ export function search(index: string, query: any, k: number): any
Như bạn có thể thấy, có rất nhiều loại "bất kỳ", không hữu ích lắm cho trải nghiệm của nhà phát triển. Hãy xem mã Rust để tìm hiểu điều gì đã xảy ra.
type NumberOfResult = usize; type Embeddings = Vec<f32>; type SerializedIndex = String; #[derive(Serialize, Deserialize, Debug)] pub struct EmbeddedResource { id: String, title: String, url: String, embeddings: Embeddings, } #[derive(Serialize, Deserialize, Debug)] pub struct Resource { pub embeddings: Vec<EmbeddedResource>, } #[wasm_bindgen] pub fn index(resource: JsValue) -> SerializedIndex { /* snip */ } #[wasm_bindgen] pub fn search(index: &str, query: JsValue, k: NumberOfResult) -> JsValue { // snip }
Chuỗi, lát cắt và số nguyên không dấu đã tạo ra các loại chính xác trong TypeScript, nhưng " wasm_bindgen::JsValue " thì không. JsValue là đại diện wasm-bindgen của một đối tượng JavaScript.
Chúng tôi tuần tự hóa và giải tuần tự hóa JsValue để chuyển nó qua lại giữa JavaScript và Rust thông qua Wasm.
#[wasm_bindgen] pub fn index(resource: JsValue) -> String { // 💡 Deserialize JsValue in to Resource struct in Rust let resource: Resource = serde_wasm_bindgen:from_value(input).unwrap(); // snip } #[wasm_bindgen] pub fn search(index: &str, query: JsValue, k: usize) -> JsValue { // snip // 💡 Serialize search result into JsValue and pass it to WebAssembly let result = engine::search(&index, &query, k).unwrap(); serde_wasm_bindgen:to_value(&result).unwrap() }
Đó là cách tiếp cận chính thức để chuyển đổi các loại dữ liệu, nhưng rõ ràng, chúng tôi cần nỗ lực hơn nữa để hỗ trợ TypeScript.
Chuyển đổi các loại dữ liệu từ ngôn ngữ này sang ngôn ngữ khác thực sự là một mẫu phổ biến được gọi là Giao diện chức năng nước ngoài (FFI). Tôi đã khám phá các công cụ FFI như Typeshare để tự động tạo các định nghĩa TypeScript từ các cấu trúc Rust, nhưng đó chỉ là một nửa của giải pháp.
Điều chúng ta cần là một cách để khai thác quá trình biên dịch Wasm và tạo định nghĩa kiểu cho API của mô-đun Wasm. Như thế này:
#[wasm_bindgen] pub fn index(resource: Resource) -> SerializedIndex { /* snip */ } #[wasm_bindgen] pub fn search(index: SerializedIndex, query: Embeddings, k: NumberOfResult) -> SearchResult { // snip }
May mắn thay, Tsify là một thư viện nguồn mở tuyệt vời cho trường hợp sử dụng. Tất cả những gì chúng ta cần làm là bắt nguồn từ đặc điểm "Tsify" và thêm macro #[tsify] vào các cấu trúc:
type NumberOfResult = usize; type Embeddings = Vec<f32>; type SerializedIndex = String; #[derive(Serialize, Deserialize, Debug, Clone, Tsify)] #[tsify(from_wasm_abi)] pub struct EmbeddedResource { pub id: String, pub title: String, pub url: String, pub embeddings: Embeddings, } #[derive(Serialize, Deserialize, Debug, Tsify)] #[tsify(from_wasm_abi)] pub struct Resource { pub embeddings: Vec<EmbeddedResource>, } #[derive(Serialize, Deserialize, Debug, Clone, Tsify)] #[tsify(into_wasm_abi)] pub struct Neighbor { pub id: String, pub title: String, pub url: String, } #[derive(Serialize, Deserialize, Debug, Clone, Tsify)] #[tsify(into_wasm_abi)] pub struct SearchResult { neighbors: Vec<Neighbor>, } #[wasm_bindgen] pub fn index(resource: Resource) -> SerializedIndex { /* snip */ } #[wasm_bindgen] pub fn search(index: SerializedIndex, query: Embeddings, k: NumberOfResult) -> SearchResult { // snip }
Đó là nó! Hãy xem các thuộc tính "from_wasm_abi" và "into_wasm_abi".
Cả hai thuộc tính đều chuyển đổi kiểu dữ liệu Rust sang định nghĩa TypeScript. Những gì họ làm khác là hướng của luồng dữ liệu với Giao diện nhị phân ứng dụng (ABI) của Wasm.
Cả hai thuộc tính đều sử dụng serde-wasm-bindgen để triển khai chuyển đổi dữ liệu giữa Rust và JavaScript.
Chúng tôi đã sẵn sàng để xây dựng mô-đun Wasm. Khi bạn chạy "wasm-pack build", định nghĩa TypeScript được tạo tự động:
/* tslint:disable */ /* eslint-disable */ /** * @param {Resource} resource * @returns {string} */ export function index(resource: Resource): string /** * @param {string} index * @param {Float32Array} query * @param {number} k * @returns {SearchResult} */ export function search( index: string, query: Float32Array, k: number ): SearchResult export interface EmbeddedResource { id: string title: string url: string embeddings: number[] } export interface Resource { embeddings: EmbeddedResource[] } export interface Neighbor { id: string title: string url: string } export interface SearchResult { neighbors: Neighbor[] }
Tất cả các loại "bất kỳ" được thay thế bằng các giao diện mà chúng tôi đã xác định trong mã Rust✨
Các loại được tạo có vẻ tốt, nhưng có một số điểm không nhất quán. Nếu quan sát kỹ, bạn sẽ nhận thấy tham số truy vấn trong hàm tìm kiếm được định nghĩa là Float32Array.
Tham số truy vấn được định nghĩa là cùng loại với "phần nhúng" trong EmbeddedResource, vì vậy tôi hy vọng chúng có cùng loại trong TypeScript.
Nếu bạn biết lý do tại sao chúng được chuyển đổi thành các loại khác nhau, vui lòng liên hệ hoặc mở yêu cầu kéo trong Voy trên GitHub .
Voy là một công cụ tìm kiếm ngữ nghĩa mã nguồn mở trong WebAssugging. Tôi đã tạo nó để trao quyền cho nhiều dự án hơn nhằm xây dựng các tính năng ngữ nghĩa và tạo trải nghiệm người dùng tốt hơn cho mọi người trên khắp thế giới. Voy tuân theo một số nguyên tắc thiết kế:
Nó có sẵn trên npm. Bạn có thể chỉ cần cài đặt nó với trình quản lý gói yêu thích của mình và bạn đã sẵn sàng sử dụng.
# with npm npm i voy-search # with Yarn yarn add voy-search # with pnpm pnpm add voy-search
Hãy dùng thử và tôi rất vui khi nhận được phản hồi từ bạn!
Muốn kết nối?
Bài viết này ban đầu được đăng trên Trang web của Daw-Chih .