【问题标题】:Js binding for large rust object using wasm-bindgen使用 wasm-bindgen 对大型 rust 对象进行 Js 绑定
【发布时间】:2021-03-22 08:05:21
【问题描述】:

我想写一个显示大二进制文件内容的vscode扩展,用bincode写的:

#[macro_use]
extern crate serde_derive;

use std::collections::HashMap;
use std::fs::File;
use std::io::{BufReader, BufWriter};

#[derive(Serialize, Deserialize)]
pub struct MyValue {
    pub name: String,
}

#[derive(Serialize, Deserialize)]
pub struct MyStruct {
    pub data: HashMap<String, MyValue>,
}

impl MyStruct {
    pub fn dump(&self, filename: &str) -> Result<(), String> {
        let file = File::create(filename).map_err(|msg| msg.to_string())?;
        let writer = BufWriter::new(file);
        bincode::serialize_into(writer, self).map_err(|msg| msg.to_string())
    }

    pub fn load(filename: &str) -> Result<Self, String> {
        let file = File::open(filename).map_err(|msg| msg.to_string())?;
        let reader = BufReader::new(file);
        bincode::deserialize_from::<BufReader<_>, Self>(reader).map_err(|msg| msg.to_string())
    }
}

因此有一个wasm绑定:


#[wasm_bindgen]
#[derive(Clone)]
pub struct PyMyStruct {
    inner: Arc<MyStruct>,
}

#[wasm_bindgen]
impl PyMyStruct {
    pub fn new(filename: &str) -> Self {
        Self {
            inner: Arc::new(MyStruct::load(filename).unwrap()),
        }
    }

    pub fn header(self) -> Array {
        let keys = Array::new();
        for key in self.inner.data.keys() {
            keys.push(&JsValue::from_str(key));
        }
        keys
    }

    pub fn value(&self, name: &str) -> JsValue {
        if let Some(value) = self.inner.data.get(name) {
            JsValue::from_serde(value).unwrap_or(JsValue::NULL)
        } else {
            JsValue::NULL
        }
    }
}

它为 JavaScript 世界提供了一个简单的接口,以便访问该文件的内容。 使用 Arc 以防止在 JavaScript 端处理时进行昂贵的意外内存复制。 (keys 没有被标记为可变,但 rust 编译器推荐这种方式可能看起来很奇怪)

运行测试代码时:

const {PyMyStruct} = require("./corejs.js");

let obj = new PyMyStruct("../../dump.spb")
console.log(obj.header())

您收到错误消息:

错误:空指针传递给 rust

有人知道如何处理这个用例吗?

谢谢!

【问题讨论】:

    标签: rust wasm-bindgen rust-wasm


    【解决方案1】:

    这里的问题是您使用的是new PyMyStruct() 而不是PyMyStruct.new()。在 wasm-bindgen 的调试模式下,您将在运行时收到关于此的错误。使用.new() 将解决您的问题:

    let obj = PyMyStruct.new("../../dump.spb")
    

    如果您将#[wasm_bindgen(constructor)] 注释添加到new 方法,那么new PyMyStruct() 也将起作用:

    #[wasm_bindgen]
    impl PyMyStruct {
        #[wasm_bindgen(constructor)]
        pub fn new(filename: &str) -> Self {
            Self {
                inner: 1,
            }
        }
    }
    

    现在这很好:

    let obj = new PyMyStruct("../../dump.spb")
    

    【讨论】:

    • 根据你的第一个建议,我得到:``` let obj = PyMyStruct("../../dump.spb").new() ^ TypeError: Class constructor PyMyStruct cannot be invoked without ' new' ``` 但是使用 `#[wasm_bindgen(constructor)]` 就可以了。
    • 很遗憾:还有2个问题: 1.对象的方法只能调用一次。第二次你仍然得到 Error: null pointer passing to rust 2. Rust 在 wasm 中运行时无法打开本地文件,即使在 Node.js 中也是如此。
    • @Matthias 我更新了我的答案以修复您的第一个错误
    【解决方案2】:

    我通过使用 https://neon-bindings.com 而不是将 API 编译为 web-assembly 解决了这个问题。

    这里的绑定如下:

    use core;
    use std::rc::Rc;
    use neon::prelude::*;
    
    #[derive(Clone)]
    pub struct MyStruct {
        inner: Rc<core::MyStruct>,
    }
    
    declare_types! {
        pub class JsMyStruct for MyStruct {
            init(mut cx) {
                let filename = cx.argument::<JsString>(0)?.value();
    
                match core::MyStruct::load(&filename) {
                    Ok(inner) => return Ok(MyStruct{
                        inner: Rc::new(inner)
                    }),
                    Err(msg) => {
                        panic!("{}", msg)
                    }
                }
            }
    
            method header(mut cx) {
                let this = cx.this();
                let container = {
                    let guard = cx.lock();
                    let this = this.borrow(&guard);
                    (*this).clone()
                };
                let keys = container.inner.data.keys().collect::<Vec<_>>();
                let js_array = JsArray::new(&mut cx, keys.len() as u32);
                for (i, obj) in keys.into_iter().enumerate() {
                    let js_string = cx.string(obj);
                    js_array.set(&mut cx, i as u32, js_string).unwrap();
                }
                Ok(js_array.upcast())
            }
    
            method value(mut cx) {
                let key = cx.argument::<JsString>(0)?.value();
                let this = cx.this();
                let container = {
                    let guard = cx.lock();
                    let this = this.borrow(&guard);
                    (*this).clone()
                };
                if let Some(data) = container.inner.data.get(&key) {
                    return Ok(neon_serde::to_value(&mut cx, data)?);
                } else {
                    panic!("No value for key \"{}\" available", key);
                }
            }
        }
    }
    
    register_module!(mut m, {
        m.export_class::<JsMyStruct>("MyStruct")?;
        Ok(())
    });
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-15
      • 2020-09-11
      • 1970-01-01
      • 2020-04-13
      • 2022-01-23
      • 2013-02-04
      • 2022-08-15
      • 1970-01-01
      相关资源
      最近更新 更多