Hello World!
This guide will walk you through writing, building, and running your first Neon project. We'll try to walk you through each step carefully, but if you want to skip ahead, you can always go straight to the full demo in the examples repository.
Our first project will be a tiny module that returns a number indicating how much hardware multithreading the current machine supports. If you're not familiar with multithreading, don't panic! We'll be using Sean McArthur's num_cpus library to do all the heavy lifting for us, and we'll just return the number it gives us.
But even this simple example already demonstrates some of Neon's usefulness: Rust's crate ecosystem is younger than npm but growing quickly and already full of useful and unique libraries. A library like num_cpus
could be useful, for example, as a hint for tuning the size of a Web Worker pool in an Electron app.
Creating a New Project
The first thing we have to do is create our new thread-count
Neon project:
neon new thread-count
This will ask us a series of questions similar to the ones asked by npm new
. When it completes, the tool will have created a thread-count
directory with the following layout:
thread-count/
├── .gitignore
├── README.md
├── lib/
│ └── index.js
├── native/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
└── package.json
The first thing to notice about this layout is that a Neon project is a Node package. In other words, the way to think of a Neon project is:
Node on the outside, Rust on the inside.
The front-end of a Neon package is a pure JavaScript module (lib/index.js
, by default), and the back-end is a native library implemented as a Rust crate. The Rust crate lives in the native/
subdirectory of the project.
Building and Running
We haven't yet implemented anything, but just to see that neon new
produced a complete, minimal Neon project, let's try building it:
neon build --release
The build process generates a handful of files that you don't need to work with directly:
native/index.node
: the native module itself, which is loaded bylib/index.js
.native/target
andnative/artifacts.json
: cached build results, which makes rebuilds faster.
An easy way to clean up build artifacts is to run:
neon clean
Once we've built the project, we can try running it:
node
> require('.')
hello node
{}
Adding a Rust Dependency
Let's add a Rust dependency on the num_cpus crate. In native/Cargo.toml
, under the [dependencies]
section, add the following line:
num_cpus = "1.4.0"
This tells Cargo, Rust's build tool, to fetch a version of the num_cpus
crate that is semver-compatible with 1.4.0
. (The package.json
equivalent would be "num_cpus": "^1.4.0"
.)
Implementing our Function
Now let's edit the Rust code to make use of the new dependency. First we have to declare the use of the the num_cpus
crate:
extern crate num_cpus;
Next we can replace the sample hello
function that was generated by neon new
with the function we actually want. Instead of returning a string, our function should return a JavaScript number. So we'll use cx.number()
helper. Since cx.number()
expects a Rust f64
(i.e., a 64-bit floating-point number), and num_cpus::get()
returns a usize
(i.e., a pointer-sized integer), we'll use Rust'sas
operator to cast to convert the integer to floating-point:
use neon::prelude::*;
fn thread_count(mut cx: FunctionContext) -> JsResult<JsNumber> {
Ok(cx.number(num_cpus::get() as f64))
}
A few more things to note about this code:
- The
cx
argument tothread_count
: this contains information about the function call, such as the arguments and the value ofthis
. - The
JsResult
output type: this is a RustResult
type that indicates whether the function returned (Ok
) or threw a JavaScript exception (Err
). You can learn more in the Handling Errors guide. It also tracks the lifetime of the returned handle. You can read more about handles in the Handles and Memory guide. - The
cx.number()
function tells the JavaScript garbage collector that we need to keep the value we allocate alive long enough to return it to the caller ofthread_count
. You can learn more about memory management in the Handles and Memory guide.
Finally, we'll modify the code that neon new
created for us to set up the module exports with this function instead of the initial "hello world" function it created for us:
register_module!(mut m, {
m.export_function("threadCount", thread_count)
});
This tells Neon to initialize the module when it's first loaded by creating a JavaScript function implemented with the thread_count
function we defined above and exporting it as a module property named "threadCount"
.
You can see the full lib.rs
file in the examples repository.
Exporting our Function
Now that the Rust code is implemented, all we have left to do is export it from the project's public module. The native module exported a threadCount
property, so we'll just make that function our entire public module:
const addon = require('../native');
module.exports = addon.threadCount;
Try it Out!
Now we should be able to rebuild the project with neon build --release
again:
neon build --release
This will create a release build for us. Assuming we didn't make any mistakes, we can test out our new Neon module at the Node console from the root of our project directory:
node
> var threadCount = require('.')
> threadCount()
4
Keep in mind that the result of calling threadCount()
will vary based on the machine you run this demo on—by design!