Appearance
Error Handling
Fresh 🌱Neon maps Rust's Result onto JavaScript's exception model. A Neon function that returns JsResult<T> either returns a value (Ok) or throws a JavaScript exception (Err). Understanding this mapping is the key to clean error handling.
JsResult and the ? operator
JsResult<'a, T> is an alias for a Rust Result whose error case represents a thrown JavaScript exception. Any operation that can throw (reading an argument, getting a property, calling a function) returns a result, so you propagate failures with the ? operator:
rust
fn read_prop(mut cx: FunctionContext) -> JsResult<JsValue> {
let obj = cx.argument::<JsObject>(0)?; // throws TypeError if arg 0 is not an object
let val = obj.get(&mut cx, "name")?; // propagates any error from the getter
Ok(val)
}When ? short-circuits, the pending JavaScript exception is already set; control returns to JS with that exception thrown.
Throwing an exception explicitly
To throw your own error, use the context's throw helpers. throw_error raises a JavaScript Error with a message:
rust
fn must_be_positive(mut cx: FunctionContext) -> JsResult<JsNumber> {
let n: f64 = cx.argument::<JsNumber>(0)?.value(&mut cx);
if n < 0.0 {
return cx.throw_error("expected a non-negative number");
}
Ok(cx.number(n.sqrt()))
}throw_error returns a result in the Err state, so you can return it directly from a function whose body produces the matching Ok type.
Converting a Result at the throw site
When you already hold a Rust Result (for example from standard-library or crate code), ResultExt::or_throw() converts an Err into a thrown JavaScript exception and unwraps the Ok:
rust
let parsed = some_rust_result.or_throw(&mut cx)?;Errors with #[neon::export]
When you use the #[neon::export] macro with plain Rust types, return a Result and Neon throws on the JavaScript side automatically. The extract::Error type bridges common Rust errors (anything implementing the standard error trait) into JS exceptions:
rust
use neon::types::extract::Error;
#[neon::export]
fn read_file(path: String) -> Result<String, Error> {
// The `?` converts std::io::Error into a thrown JS exception
let contents = std::fs::read_to_string(path)?;
Ok(contents)
}On the JavaScript side this looks like a normal throwing function:
javascript
try {
readFile('/no/such/path');
} catch (e) {
console.error(e.message);
}Summary
| Situation | Tool |
|---|---|
| Propagate an error that already occurred | ? operator on a JsResult |
| Throw your own error | cx.throw_error("message") |
Turn a Rust Result into a throw | result.or_throw(&mut cx)? |
Return errors from #[neon::export] | Result<T, neon::types::extract::Error> |
Errors are values, not panics
Prefer throwing JavaScript exceptions over Rust panic!. A panic across the FFI boundary is far more disruptive than a clean thrown error that JavaScript can catch.