Rust combines low-level control over performance with high-level convenience and safety guarantees.
Rust 1.0 just got released. Let’s see how and why can we use it with Node!
Rust Features
Before jumping into Rust and its ecosystem, let’s step one back and take a look on what Rust promises.
- Rust is a systems programming language
- It focuses on speed, safety and concurrency
- Great fit to write programs with specific time requirements
- Ability to write low-level code, like device drivers
- No garbage collector
Why Using Rust with Node.js?
There can be a number of reasons where Rust can be complementary to a Node.jsNode.js is an asynchronous event-driven JavaScript runtime and is the most effective when building scalable network applications. Node.js is free of locks, so there's no chance to dead-lock any process. application. Also, you may ask yourself: why should I use Rust when we already have addons with great helper modules like nan? The short answer for that is if you need to have access to V8/libuv, then you should always use the native addons if not, then you should go for the best tool for the job.
Performance Gains
Blocking the event loop – something that I am sure you don’t want to do. If you have lots of synchronous operations, it can easily happen that you will block it.
These operations can run in a separate process, using Rust for example. Later on we will take a look on how you can do something like this.
For the eager ones: you can see a chart below showing how many seconds did it take with Rust, Node.js and C to generate the 40th Fibonacci number a 100 times.
Generating the 40th Fibonacci Number a 100 times, the lower, the better – the point is not to show if one is faster than the other, just to showcase that Rust can be as fast as C
Example Project
As an example project, let’s create the one showcased before: a small application that can generate Fibonacci numbers.
Installing Rust
If you are using a Mac, you can simply install it using Brew, or visit http://www.rust-lang.org/ to download the installer for you operating system. Rust comes with Cargo – Cargo is kind of like NPMnpm is a software registry that serves over 1.3 million packages. npm is used by open source developers from all around the world to share and borrow code, as well as many businesses. There are three components to npm: the website the Command Line Interface (CLI) the registry Use the website to discover and download packages, create user profiles, and... in the Node world, except it not just manages your dependencies but also capable of building your projects as well.
Using Cargo
The very first thing that we need when using Cargo is Cargo.toml
(like package.json
in the Node world).
A simple Cargo.toml
file may look like this:
[package]
name = "fibonacci"
version = "1.0.0"
authors = [ "Gergely Nemeth <gergely@example.io>" ]
The second thing, of course, the Rust source file – src/main.rs
:
fn main() {
println!("Hello, Node!");
}
You can build and run this very basic application using cargo run
, or if you just want to build it then cargo build
.
Calling Rust from Node.js
Now we can build any Rust project – it is time to integrate it with Node.js.
For this purpose, we are going to use something called Foreign Function Interface, FFI in short.
A foreign function interface (FFI) is a mechanism by which a program written in one programming language can call routines or make use of services written in another
To achieve this, we have to modify our Rust project a little. First of all, I am adding a function called fibonacci
to the project:
fn fibonacci(x: i32) -> i32 {
if x <= 2 {
return 1;
} else {
return fibonacci(x - 1) + fibonacci(x - 2);
}
}
If we would simply build it and try to run it from Node, it won’t work. Rust changes the name of the function in the compiled output and by default these functions are not publicly accessible. To fix these issues, try this:
#[no_mangle]
pub extern fn fibonacci(x: i32) -> i32 {
if x <= 2 {
return 1;
} else {
return fibonacci(x - 1) + fibonacci(x - 2);
}
}
The #[no_mangle]
instructs the Rust compiler not to modify the name of the function, the pub
makes it publicly available outside of this module as well, and the extern
tells the compiler to make it callable through the C interface. Not a lot of changes, right?
One last thing that we have to add to our Cargo.toml
file:
[lib]
name = "fibonacci"
crate-type = ["dylib"]
These extra lines tell the compiler that we want to create a dynamic library from the source as well, what can be ran from any language, including Node.js as well.
Ok, let’s go to the Node.js part! To call this function from Node, we have to use the ffi module, like this:
var ffi = require('ffi');
var lib = ffi.Library(path.join(__dirname, '../target/release/libembed'), {
fibonacci: ['int', ['int']]
});
var num = lib.fibonacci(20);
That’s it, you are calling Rust from Node.js now!
If you want to take a look at the full project, you can do that at the RisingStack GitHub repo. This project also includes a small benchmarking util for Rust, C and JavaScript implementations of the Fibonacci number generator.
Outro
Adding Rust to your project is not black magic – if you need to offload computational heavy operations from Node.js, Rust may be one of your best options. As a closing remark, keep in mind the following:
There’s a significant cost in FFI calls, so make them worth it. (Nathan Rajlich)
What are your thoughts on Rust? Share your experience in the comments below.