ubolonton / emacs-module-rs

Rust binding and tools for Emacs's dynamic modules
340 stars 22 forks source link

[discussion] Is it possible to pass a hashmap from Emacs to Rust? #12

Closed justinbarclay closed 5 years ago

justinbarclay commented 5 years ago

I'm working on a package that's passing a hashmap serialized as JSON back and forth between my rust library and Emacs. Unfortunately, this can be a pretty slow processes on large JSON objects. I was wondering if it's possible to pass in and return an Emacs hashmap directly?

Or are there alternative approaches for emulating a hashmap? Should I do something akin to this: https://github.com/ubolonton/emacs-module-rs/blob/master/test-module/src/hash_map.rs

ubolonton commented 5 years ago

It's possible to take and return an Emacs's hash-table. For example:

#[defun]
fn foo(table: Value) -> Result<Value> {
    let env = map.env;
    let key: Value = ...;
    let value: Value = ...;
    env.call("puthash", &[key, value, table])?;
    Ok(table)
}

However, doing so results in a lot of back-n-forth between Lisp and Rust, especially when it's a big, nested piece of data.

Embedding Rust's hashmap in a user-ptr with custom accessors and mutators (like in that test) avoid that cost, with the tradeoff of being less flexible.

From the discussion in https://github.com/emacs-lsp/lsp-mode/issues/676, I think embedding is usually preferable. It depends on the specific use case though. Can you provide more details about yours, and/or the code of the package, if possible?

justinbarclay commented 5 years ago

Awesome, thanks for the breakdown.

Right now I'm just exporting a single function: https://github.com/justinbarclay/parinfer-rust/blob/emacs-integration/src/emacs_wrapper.rs

And here is an example of it in use: https://github.com/justinbarclay/parinfer-rust/blob/emacs-integration/plugin/parinfer.el#L137

ubolonton commented 5 years ago

I think since the processing is in the same process, you should use the APIs that work on Request and Answer directly, instead of the JSON wrapper.

On an unrelated note, it seems like the design of parinfer-rust is geared towards the batch formatting use case, with strings as input/output instead of a mutable buffer and parse state. That could be pretty inefficient on its own.