SoatDev IT Consulting
SoatDev IT Consulting
  • About us
  • Expertise
  • Services
  • How it works
  • Contact Us
  • News
  • August 8, 2023
  • Rss Fetcher

A quick experiment

Photo by Tolga Ulkan on Unsplash

I’ve been working with JavaScript as my daily job for a few years now. Lately, I started fiddling with Rust, to learn new concepts and understand what the hype is all about (see the Stack Overflow Survey 2023)

It looks like more and more JS tools are written in Rust lately:

Why would someone want to do this?

  • JavaScript has wide adoption in the industry but performance is not great at all
  • Rust has killer performance but has a harder learning curve

Putting both of them together is a way to get the best of both worlds-develop in an easy-to-learn and widespread language, JavaScript, and optimize critical parts with Rust.

So, how can Rust and JavaScript be brought together in a codebase?

Let’s have a look at napi-rs!

npx @napi-rs/cli new

The default project defines the following src/lib.rs file:

#![deny(clippy::all)]

#[macro_use] extern crate napi_derive;

#[napi] pub fn sum(a: i32, b: i32) -> i32 { a + b }

Running yarn build will create:

  • A node addon binary file: <project name>.<target>.node
  • An index.js that defines the JS bindings for the node addon binary: it loads the right binary and exports the sum function
  • An index.d.ts that exposes type definitions for index.js

Using the sum function is straightforward:

const { sum } = require("./index.js"); console.log(sum(40, 2));

Serde is a Rust library that allows serializing to and deserializing from various data formats, including JSON.

Let’s compare how fast we can go with Serde + napi-rs compared to JS default JSON parsing.

For this test, we’ll parse objects with the following shape:

{
name: string
phoneNumbers: string[]
}

The Rust code for the Serde-based parse function looks like this:

#![deny(clippy::all)]

#[macro_use] extern crate napi_derive;

use serde::{Deserialize, Serialize}; #[napi(constructor)]

#[derive(Serialize, Deserialize)]
pub struct Person {
pub name: String,
pub phones: Vec<String>,
}

#[napi]
pub fn parse(data: String) -> napi::Result<Person> {
Ok(serde_json::from_str(&data)?)
}

Configuration
Some configuration has to be done in
Cargo.toml on both serde and napi to make this code work:

Add serde-json to the list of features for napi:

napi = { …, features = [“napi4″,”serde-json”,] }

Add the derive feature to serde:

serde = { …, features = [“derive”] }

Error handling

Rust and JS have very different error-handling systems.

  • Exceptions for JS
  • The Result type for Rust

napi::Result<T> allows to turn a Rust Error into a JS exception automatically.

Running the parse function from a JS file with an invalid JSON throws an exception:

const index = require("./index.js");

const parsedValue = index.parse("Invalid JSON");
console.log(parsedValue);
const parsedValue = index.parse("Invalid JSON");
^

Error: expected value at line 1 column 1
at ... { code: 'InvalidArg' }

parse also throws an exception if the received data doesn’t have the right shape:

const index = require("./index.js");

const parsedValue = index.parse('{"name": "John Doe"}');
console.log(parsedValue);
const parsedValue = index.parse('{"name": "John Doe"}');
^

Error: missing field `phones` at line 1 column 20
at ... { code: 'InvalidArg' }

A quick micro-benchmark

Since Serde does parsing and validation, here is the function parse will be compared against:

const yup = require("yup");

const schema = yup.object({
name: yup.string(),
phones: yup.array(yup.string()),
});
const parseJs = (data) => {
const d = JSON.parse(data);
return schema.validateSync(d);
};

And the benchmark function:

const benchmark = (name, f, size) => {
console.time(name);
for (let i = 0; i < size; i++) {
f(`{ "name": "John Doe", "phones": [ "+33 123456789" ] }`);
}
console.timeEnd(name);
};

On 1M calls, the results are as follows:

JS: 8.356s
Rust: 2.966s

Running tests on 1000 iterations with objects of different sizes:

|`phones` array size|Rust (ms)|JS (ms)|
|-------------------|---------|-------|
| 1| 4,758| 51,946|
| 10| 9,334| 41,266|
| 100| 61,586|220,485|
| 1000| 499,942| 1987|
| 10000| 5338| 22685|

In this benchmark, the Rust parsing function seems to be 4x faster than the JS one.

Conclusion

napi-rs allows to easily use the power of Rust in a JavaScript codebase:

  • Exporting functions from Rust doesn’t require much work and they can be imported in JavaScript seamlessly.
  • It looks like there is a performance win in doing this (even though microbenchmarks should not be given too much credit)
  • This setup requires a build step that makes using it less convenient than a JS-only setup

The code I used for this experiment can be found on this GitHub repository. Thanks for reading.

Originally published at thoughtful-fiddler.dev


Exploring JavaScript-Rust Interoperability With napi-rs was originally published in Better Programming on Medium, where people are continuing the conversation by highlighting and responding to this story.

Previous Post
Next Post

Recent Posts

  • Why a new anti-revenge porn law has free speech experts alarmed 
  • Week in Review: Notorious hacking group tied to the Spanish government
  • Structured frameworks for complex systems
  • Dungeons, Dragons, and Numbers
  • My favorite paper: H = W

Categories

  • Industry News
  • Programming
  • RSS Fetched Articles
  • Uncategorized

Archives

  • May 2025
  • April 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • August 2024
  • July 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023

Tap into the power of Microservices, MVC Architecture, Cloud, Containers, UML, and Scrum methodologies to bolster your project planning, execution, and application development processes.

Solutions

  • IT Consultation
  • Agile Transformation
  • Software Development
  • DevOps & CI/CD

Regions Covered

  • Montreal
  • New York
  • Paris
  • Mauritius
  • Abidjan
  • Dakar

Subscribe to Newsletter

Join our monthly newsletter subscribers to get the latest news and insights.

© Copyright 2023. All Rights Reserved by Soatdev IT Consulting Inc.