Error Handling
Emacs Lisp's error handling mechanism uses non-local exits. Rust uses Result enum. emacs-module-rs converts between the 2 at the Rust-Lisp boundaries (more precisely, Rust-C).
The chosen error type is the Error struct from failure crate:
# #![allow(unused_variables)] #fn main() { pub type Result<T> = result::Result<T, failure::Error>; #}
Handling Lisp errors in Rust
When calling a Lisp function, it's usually a good idea to propagate signaled errors with the ? operator, letting higher level (Lisp) code handle them. If you want to handle a specific error, you can use error.downcast_ref:
# #![allow(unused_variables)] #fn main() { match env.call("insert", &[some_text]) { Err(error) => { // Handle `buffer-read-only` error. if let Some(&Signal { ref symbol, .. }) = error.downcast_ref::<ErrorKind>() { let buffer_read_only = env.intern("buffer-read-only")?; // `symbol` is a `TempValue` that must be converted to `Value`. let symbol = unsafe { Ok(symbol.value(env)) }; if env.eq(symbol, buffer_read_only) { env.message("This buffer is not writable!")?; return Ok(()) } } // Propagate other errors. Err(error) }, v => v, } #}
Note the use of unsafe to extract the error symbol as a Value. The reason is that, ErrorKind::Signal is marked Send+Sync, for compatibility with failure, while Value is lifetime-bound by env. The unsafe contract here requires the error being handled (and its TempValue) to come from this env, not from another thread, or from a global/thread-local storage.
Handling Rust errors in Lisp
In addition to standard errors, Rust module functions can signal Rust-specific errors, which can also be handled by condition-case:
rust-error: The message isRust error. This covers all generic Rust-originated errors.rust-wrong-type-user-ptr: The message isWrong type user-ptr. This happens when Rust code is passed auser-ptrof a type it's not expecting. It is a sub-type ofrust-error.# #![allow(unused_variables)] #fn main() { // May signal if `value` holds a different type of hash map, // or is a `user-ptr` defined in a non-Rust module. let r: &RefCell<HashMap<String, String>> = value.into_rust()?; #}
Panics
Unwinding from Rust into C is undefined behavior. emacs-module-rs prevents that by using catch_unwind at the Rust-to-C boundary, converting a panic into a Lisp's error signal of type rust-panic. Note that it is not a sub-type of rust-error.
Catching values thrown by Lisp
This is similar to handling Lisp errors. The only difference is ErrorKind::Throw being used instead of ErrorKind::Signal.