Values
Conversions between Rust and JavaScript types.
This page introduces common values. For the complete source-backed matrix—including conversion direction, ownership, Cargo features, Option, Either, collections, paths, functions, Promises, streams, and Node-API levels—see Type conversions.
Undefined
Represent undefined in JavaScript.
#[napi]
pub fn get_undefined() -> Undefined {
()
}
// default return or empty tuple `()` are `undefined` after converted into JS value.
#[napi]
pub fn log(n: u32) {
println!("{}", n);
}
export function getUndefined(): void
export function log(n: number): void
Null
Represents null value in JavaScript.
#[napi]
pub fn get_null() -> Null {
Null
}
#[napi]
pub fn get_env(env: String) -> Option<String> {
match std::env::var(env) {
Ok(val) => Some(val),
Err(e) => None,
}
}
export function getNull(): null
export function getEnv(env: string): string | null
Option<T> accepts T, null, or undefined as an argument, but returns null for None. In an #[napi(object)] field, the default representation is an optional property and None is omitted on output; #[napi(use_nullable)] instead makes it a required T | null property. See Option, null, and undefined for the full position-dependent mapping.
Numbers
JavaScript Number type with Rust Int/Float types: u32, i32, i64, f64.
For Rust types like u64, u128, i128, checkout BigInt section.
#[napi]
pub fn sum(a: u32, b: i32) -> i64 {
i64::from(a) + i64::from(b)
}
export function sum(a: number, b: number): number
String
Represents JavaScript String type.
#[napi]
pub fn greet(name: String) -> String {
format!("greeting, {}", name)
}
export function greet(name: string): string
Boolean
Represents JavaScript Boolean type.
#[napi]
pub fn is_good() -> bool {
true
}
export function isGood(): boolean
Buffer
#[napi]
pub fn with_buffer(buf: Buffer) {
let buf: Vec<u8> = buf.into();
// do something
}
#[napi]
pub fn read_buffer(file: String) -> Result<Buffer> {
Ok(std::fs::read(file)?.into())
}
export function withBuffer(buf: Buffer): void
export function readBuffer(file: string): Buffer
Object
Represents JavaScript anonymous object values.
WARNING
Performance
The costs of Object conversions between JavaScript and Rust are higher than other primitive types.
Every call of Object.get("key") is actually dispatched to node side including two steps: fetch value, convert JS to rust value, and so is Object.set("key", v).
#[napi]
pub fn keys(obj: Object) -> Result<Vec<String>> {
Object::keys(&obj)
}
#[napi]
pub fn log_string_field(obj: Object, field: String) -> Result<()> {
println!("{}: {:?}", &field, obj.get::<String>(&field)?);
Ok(())
}
#[napi]
pub fn create_obj(env: &Env) -> Result<Object> {
let mut obj = Object::new(env)?;
obj.set("test", 1)?;
Ok(obj)
}
export function keys(obj: object): Array<string>
export function logStringField(obj: object, field: string): void
export function createObj(): object
If you want NAPI-RS to convert objects from JavaScript with the same shape defined in Rust, you can use the #[napi] macro with the object attribute.
use std::collections::HashMap;
/// #[napi(object)] requires all struct fields to be public
#[napi(object)]
pub struct PackageJson {
pub name: String,
pub version: String,
pub dependencies: Option<HashMap<String, String>>,
pub dev_dependencies: Option<HashMap<String, String>>,
}
#[napi]
pub fn log_package_name(package_json: PackageJson) {
println!("name: {}", package_json.name);
}
#[napi]
pub fn example_package_json() -> PackageJson {
PackageJson {
name: "example".to_owned(),
version: "1.0.0".to_owned(),
dependencies: None,
dev_dependencies: None,
}
}
export interface PackageJson {
name: string
version: string
dependencies?: Record<string, string>
devDependencies?: Record<string, string>
}
export function logPackageName(packageJson: PackageJson): void
export function examplePackageJson(): PackageJson
WARNING
Clone over Reference
The #[napi(object)] struct passed to a Rust fn is cloned from the JavaScript Object. Any mutation on it will not be reflected in the original JavaScript object.
#[napi(object)] is an owned plain-object shape, not a class. Use #[napi] struct for native class identity and methods, #[napi(transparent)] for a Rust newtype with the inner JavaScript representation, or #[napi(array)] for a tuple-shaped array. See Type conversions.
/// #[napi(object)] requires all struct fields to be public
#[napi(object)]
pub struct Animal {
pub name: String,
}
#[napi]
pub fn change_animal_name(mut animal: Animal) {
animal.name = "cat".to_string();
}
const animal = { name: 'dog' }
changeAnimalName(animal)
console.log(animal.name) // "dog"
Array
Because Array values in JavaScript can hold elements with different types, but Rust Vec<T>
can only contain elements of the same type, there are two different ways to handle array types.
WARNING
Performance
Because JavaScript Array type is actually backed by Object, the performance of manipulating Arrays is the same as Objects.
The conversion between Array and Vec<T> is even heavier, which is in O(n) complexity.
#[napi]
pub fn arr_len(arr: Array) -> u32 {
arr.len()
}
#[napi]
pub fn get_tuple_array(env: &Env) -> Result<Array> {
let mut arr = env.create_array(2)?;
arr.insert(1)?;
arr.insert("test")?;
Ok(arr)
}
#[napi]
pub fn vec_len(nums: Vec<u32>) -> Result<u32> {
u32::try_from(nums.len())
.map_err(|_| Error::new(Status::InvalidArg, "Array is too large"))
}
#[napi]
pub fn get_nums() -> Vec<u32> {
vec![1, 1, 2, 3, 5, 8]
}
export function arrLen(arr: unknown[]): number
export function getTupleArray(): unknown[]
export function vecLen(nums: Array<number>): number
export function getNums(): Array<number>
BigInt
This requires the napi6 feature.
WARNING
The only way to pass BigInt in Rust is using BigInt type. But you can
return BigInt, i64n, u64, i128, u128. Return i64 will be treated
as JavaScript number, not BigInt.
TIP
The reason why Rust functions can't receive i128 u128 u64 i64n as
arguments is that they may lose precision when converting JavaScript BigInt
into them. You can use BigInt::get_u128, BigInt::get_i128, etc. to get the
value in BigInt. The return value of these methods also indicates whether
precision is lost.
/// the return value of `get_u128` is (signed: bool, value: u128, lossless: bool)
#[napi]
pub fn bigint_add(a: BigInt, b: BigInt) -> Result<u128> {
let (a_signed, a_value, a_lossless) = a.get_u128();
let (b_signed, b_value, b_lossless) = b.get_u128();
if a_signed || b_signed || !a_lossless || !b_lossless {
return Err(Error::new(
Status::InvalidArg,
"both values must be lossless, non-negative u128 integers",
));
}
a_value.checked_add(b_value).ok_or_else(|| {
Error::new(Status::InvalidArg, "u128 addition overflowed")
})
}
#[napi]
pub fn create_big_int_i128() -> i128 {
100
}
export function bigintAdd(a: bigint, b: bigint): bigint
export function createBigIntI128(): bigint
TypedArray
TIP
Unlike JavaScript Object, the TypedArray passed into Rust fn is a
Reference. No data Copy or Clone will be performed. Every mutation on
the TypedArray will be reflected to the original JavaScript TypedArray.
#[napi]
pub fn convert_u32_array(input: Uint32Array) -> Vec<u32> {
input.to_vec()
}
#[napi]
pub fn create_external_typed_array() -> Uint32Array {
Uint32Array::new(vec![1, 2, 3, 4, 5])
}
#[napi]
pub fn mutate_typed_array(mut input: Float32Array) {
for item in unsafe { input.as_mut() } {
*item *= 2.0;
}
}
export function convertU32Array(input: Uint32Array): Array<number>
export function createExternalTypedArray(): Uint32Array
export function mutateTypedArray(input: Float32Array): void
import { convertU32Array, mutateTypedArray } from './index.js'
convertU32Array(new Uint32Array([1, 2, 3, 4, 5])) // [1, 2, 3, 4, 5]
const values = new Float32Array([1, 2, 3, 4, 5])
mutateTypedArray(values)
console.log(values) // Float32Array(5) [ 2, 4, 6, 8, 10 ]