Skip to content

Functions

Fresh 🌱

Neon's main way of connecting Rust and JavaScript is letting you define functions implemented in Rust. A Neon function looks and acts like a regular JavaScript function, but its body is Rust.

Defining functions

A Neon function is a Rust function of type fn(FunctionContext) -> JsResult<T>, where T is any type implementing the Value trait:

rust
fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
    Ok(cx.string("hello"))
}
  • cx: FunctionContext gives access to the JavaScript runtime (arguments, this, value constructors).
  • JsResult<T> signals the function may throw a JavaScript error. Here we just build a string and wrap it in Ok.

Export it from your module:

rust
#[neon::main]
pub fn main(mut cx: ModuleContext) -> NeonResult<()> {
    cx.export_function("hello", hello)?;
    Ok(())
}

main returns a result too, because export_function interacts with the module object and could throw. The ? operator propagates any error. Then from JavaScript:

javascript
const { hello } = require('./index');
console.log(hello()); // prints "hello"

Accessing arguments

Read positional arguments with FunctionContext::argument():

rust
fn create_pair(mut cx: FunctionContext) -> JsResult<JsObject> {
    let x: Handle<JsValue> = cx.argument(0)?;
    let y: Handle<JsValue> = cx.argument(1)?;

    let obj = cx.empty_object();
    obj.set(&mut cx, "x", x)?;
    obj.set(&mut cx, "y", y)?;
    Ok(obj)
}

Checking argument types

Choose a more specific type than JsValue to both check and cast the argument. If a check fails, the function throws a TypeError:

rust
fn create_book(mut cx: FunctionContext) -> JsResult<JsObject> {
    let title = cx.argument::<JsString>(0)?;
    let author = cx.argument::<JsString>(1)?;
    let year = cx.argument::<JsNumber>(2)?;

    let obj = cx.empty_object();
    obj.set(&mut cx, "title", title)?;
    obj.set(&mut cx, "author", author)?;
    obj.set(&mut cx, "year", year)?;
    Ok(obj)
}
javascript
try {
  createBook(null, null, null);
} catch (e) {
  console.log(e); // TypeError
}

Optional arguments

FunctionContext::argument_opt() extracts an argument that may be absent:

rust
fn create_job(mut cx: FunctionContext) -> JsResult<JsObject> {
    let company = cx.argument::<JsString>(0)?;
    let title = cx.argument::<JsString>(1)?;
    let start_year = cx.argument::<JsNumber>(2)?;
    let end_year = cx.argument_opt(3);

    let obj = cx.empty_object();
    obj.set(&mut cx, "company", company)?;
    obj.set(&mut cx, "title", title)?;
    obj.set(&mut cx, "startYear", start_year)?;

    if let Some(end_year) = end_year {
        obj.set(&mut cx, "endYear", end_year)?;
    } else {
        let null = cx.null();
        obj.set(&mut cx, "endYear", null)?;
    }

    Ok(obj)
}

Calling JavaScript functions

Call a JS function from Rust with JsFunction::call_with(). This example pulls parseInt off the global object and calls parseInt("42"):

rust
let parse_int: Handle<JsFunction> = cx.global().get(&mut cx, "parseInt")?;

let x: Handle<JsNumber> = parse_int
    .call_with(&mut cx)
    .arg(cx.string("42"))
    .apply(&mut cx)?;

Calling constructor functions

Invoke a JS function as a constructor (as if with new) using JsFunction::construct_with(). This example calls new URL("..."):

rust
let url: Handle<JsFunction> = cx.global().get(&mut cx, "URL")?;

let obj = url
    .construct_with(&mut cx)
    .arg(cx.string("https://neon-bindings.com"))
    .apply(&mut cx)?;

A newer builder: bind()

Recent Neon also offers a bind() builder for calling and constructing functions with typed argument conversion. See Calling JavaScript from Rust for that style plus Root for holding a function across async boundaries.

To export functions with plain Rust types instead of FunctionContext, see the Export Functions SOP and the #[neon::export] macro.