Functions
Neon's main way of connecting Rust and JavaScript is by allowing you to define functions that are implemented in Rust.
Defining Functions#
A Neon function looks and acts to JavaScript like a regular JavaScript function, but its behavior is written in Rust. Creating a Neon function requires first defining a Rust function of type fn(FunctionContext) -> JsResult<T> where T can be any type that implements the Value trait:
fn hello(mut cx: FunctionContext) -> JsResult<JsString> { Ok(cx.string("hello"))}The cx argument is a FunctionContext, which provides the Neon function with access to the JavaScript runtime. The JsResult result type indicates that a Neon function may throw a JavaScript error. In this example, we just construct a string and return it, so we immediately wrap the outcome in an Ok result. A more involved Neon function might call other functions or interact with objects in ways that could end up triggering errors.
The most common way to define a Neon function from hello is to export it from our module using ModuleContext::export_function():
#[neon::main]pub fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("hello", hello)?; Ok(())}Notice that the main function also returns a result, because it can trigger JavaScript errors. In this code, calling the export_function() method could potentially throw an error since it's interacting with the module object. We use Rust's ? operator to return early and propagate any errors that get thrown.
A JavaScript module can then call the hello function by importing from the Neon module:
const { hello } = require('./index');
console.log(hello()); // prints "hello"!Accessing Arguments#
A Neon function can access its arguments by calling FunctionContext::argument():
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#
You can conveniently check the type of a Neon function argument and cast it to the corresponding Rust type by choosing a more specific type than JsValue. This example constructs an object representing metadata about a book, first checking the first two arguments to be strings and the third argument to be a number:
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)}
#[neon::main]pub fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("createBook", create_book)?; Ok(())}If any of the checks fails, the createBook function throws a TypeError:
const { createBook } = require('./index');
try { createBook(null, null, null);} catch (e) { console.log(e); // TypeError}Optional Arguments#
The FunctionContext::argument_opt() method makes it possible to extract an optional argument. This example creates an entry in a resume's job history, where the end year may not be present (indicating the person's current job):
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 Functions#
You can call a JavaScript function from Rust with JsFunction::call_with(). This example extracts the parseInt function and calls it on a string:
// Extract the parseInt function from the global objectlet parse_int: Handle<JsFunction> = cx.global().get(&mut cx, "parseInt")?;
// Call parseInt("42")let x: Handle<JsNumber> = parse_int .call_with(&mut cx) .arg(cx.string("42")) .apply(&mut cx)?;The parse_int.call_with() method takes a runtime context and produces a CallOptions struct that can be used to specify the arguments to the function. In this case, we specify just one argument: a JavaScript string constructed from the Rust string "42".
Calling Constructor Functions#
You can call a JavaScript function as a constructor, as if with the new operator, with JsFunction::construct_with(). This example extracts the URL constructor and invokes it with a URL string:
// Extract the URL constructor from the global objectlet url: Handle<JsFunction> = cx.global().get(&mut cx, "URL")?;
// Call new URL("https://neon-bindings.com")let obj = url .construct_with(&mut cx) .arg(cx.string("https://neon-bindings.com")) .apply(&mut cx)?;Similar to call_with() above, the url.construct_with() method takes a runtime context and produces a ConstructOptions struct that can be used to specify the arguments to the constructor call. In this case, we specify just one argument: a JavaScript string constructed from the Rust string "https://neon-bindings.com".