Call Rust From Js Using Neon

In a previous post Writing python extension with rust I wrote about calling Rust from Python. This time, I wanted to do the same from JavaScript. I did it using a great module Neon. It does the exact same thing that maturin does for Python.

Here’s how you can achieve this. Setup

You are all set to start a Neon project.

for example

  npm init neon crabby_binding

And you will get output, something like

girish in ~/git/rust/neon-projs 
πŸ•™ 00:00:00 βœ–  npm init neon crabby_bindings
Need to install the following packages:
  create-neon@0.2.0
Ok to proceed? (y) y
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (crabby_bindings) 
version: (0.1.0) 
description: test_crabby_bindings
git repository: 
keywords: 
author: girish
license: (ISC) 
About to write to /home/girish/git/rust/neon-projs/crabby_bindings-LlRCyJ/package.json:

{
  "name": "crabby_bindings",
  "version": "0.1.0",
  "main": "index.node",
  "scripts": {
    "build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
    "build-debug": "npm run build --",
    "build-release": "npm run build -- --release",
    "install": "npm run build-release",
    "test": "cargo test"
  },
  "devDependencies": {
    "cargo-cp-artifact": "^0.1"
  },
  "description": "test_crabby_bindings",
  "author": "girish",
  "license": "ISC"
}


Is this OK? (yes) 
✨ Created Neon project `crabby_bindings`. Happy πŸ¦€ hacking! ✨

This will bootstrap a project and you are all set to write rust.

The project structure for this looks like

girish in ~/git/rust/neon-projs 
πŸ•™ 00:00:01 ❯ cd crabby_bindings/
girish in git/rust/neon-projs/crabby_bindings via  v18.16.1 πŸ¦€ v1.66.0 
πŸ•™ 00:00:01 ❯ tree .
.
β”œβ”€β”€ Cargo.toml
β”œβ”€β”€ package.json
β”œβ”€β”€ README.md
└── src
    └── lib.rs

2 directories, 4 files

The default contents in the src/lib.rs look like

use neon::prelude::*;

fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
    Ok(cx.string("hello node"))
}

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

Also, the Default contents of Cargo.toml looks like

[package]
name = "crabby_bindings"
version = "0.1.0"
description = "test_crabby_bindings"
authors = ["girish"]
license = "ISC"
edition = "2018"
exclude = ["index.node"]

[lib]
crate-type = ["cdylib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

[dependencies.neon]
version = "0.10"
default-features = false
features = ["napi-6"]

Just based on these two files, we can say that, it

So what exactly it does:

After exploring the Rust side, let’s examine the package.jsonβ€”our main JavaScript configuration.

So let’s try running

npm install

as it will generate a release build for this module.

Once this is done, we get index.node in the same directory

girish in git/rust/neon-projs/crabby_bindings via  v18.16.1 πŸ¦€ v1.66.0 
πŸ•™ 00:00:02 βœ–  ls
Cargo.lock  Cargo.toml  index.node  node_modules  package.json  package-lock.json  README.md  src  target

We can change the name of this output file in the package.json.

This module can be directly used in a js file. for example,

// import crabby, our module written in rust
const crabby = require('.');
// print the contents of crabby.
console.log("crabby module: ", crabby);
// call a function from crabby.
var crabby_hello_output = crabby.hello();
console.log("output of the crabby.hello(): ", crabby_hello_output);

And the output of this looks like:

girish in git/rust/neon-projs/crabby_bindings via  v18.16.1 πŸ¦€ v1.66.0 
πŸ•™ 00:00:03 βœ–  ls
Cargo.lock  Cargo.toml  index.node  node_modules  package.json  package-lock.json  README.md  src  target test.js
girish in git/rust/neon-projs/crabby_bindings via  v18.16.1 πŸ¦€ v1.66.0 
πŸ•™ 00:00:03 ❯ node test.js 
crabby module:  { hello: [Function: crabby_bindings::hello] }
output of the crabby.hello():  hello node

While our example/boilerplate code demonstrates a synchronous Rust function, integrating asynchronous Rust functions, especially those relying on frameworks like tokio, can be more complex. Stay tuned for a follow-up post where we’ll dive deeper into asynchronous Rust-JavaScript interoperability.