Appearance
Objects
Fresh 🌱Most data in JavaScript is an object - in fact, everything that is not a primitive type is an object. In Neon, all object types implement the Object trait, which lets you interact with an object's properties. The plain JavaScript Object ("vanilla object") is available through the JsObject type.
Creating objects
cx.empty_object() creates a new JsObject:
rust
let obj: Handle<JsObject> = cx.empty_object();Getting properties
Object::get() reads a property at runtime:
rust
let obj: Handle<JsObject> = cx.empty_object();
// Get the `toString` property (inherited from the prototype chain here)
let prop: Handle<JsValue> = obj.get(&mut cx, "toString")?;Setting properties
Object::set() writes a property at runtime:
rust
let obj = cx.empty_object();
let age = cx.number(35);
obj.set(&mut cx, "age", age)?;Converting a Rust struct to a JavaScript object
A common task is mapping a Rust struct onto a JS object. Start with a type:
rust
struct Book {
pub title: String,
pub author: String,
pub year: u32,
}Define the conversion as a method so callers get pleasant book.to_object(&mut cx) syntax:
rust
impl Book {
fn to_object<'a>(&self, cx: &mut FunctionContext<'a>) -> JsResult<'a, JsObject> {
let obj = cx.empty_object();
let title = cx.string(&self.title);
obj.set(cx, "title", title)?;
let author = cx.string(&self.author);
obj.set(cx, "author", author)?;
let year = cx.number(self.year);
obj.set(cx, "year", year)?;
Ok(obj)
}
}About that 'a lifetime
The <'a> annotation tells the Rust compiler that the returned object (lifetime 'a) is managed by the same runtime context passed in (also lifetime 'a). This is how Neon guarantees you can never accidentally hold an unsafe reference to a JS value after the runtime has moved on. If lifetimes are new to you, treat this signature as a template - the compiler keeps you safe.
Make it generic over any context
The method only uses generic Context methods, so you can accept any Context implementation rather than the specific FunctionContext:
rust
impl Book {
fn to_object<'a>(&self, cx: &mut impl Context<'a>) -> JsResult<'a, JsObject> {
// same body as before
}
}Now it also works with a ModuleContext, so you can build and export an object at module init:
rust
#[neon::main]
pub fn main(mut cx: ModuleContext) -> NeonResult<()> {
let book = Book {
title: "Chadwick the Crab".to_string(),
author: "Priscilla Cummings".to_string(),
year: 2009,
};
let obj = book.to_object(&mut cx)?;
cx.export_value("chadwick", obj)?;
Ok(())
}The property builder pattern
For building an object inline, Neon also supports a chained prop builder:
rust
let result = cx.empty_object()
.prop(&mut cx, "number").set(num)?
.prop("text").set(str)?
.prop("flag").set(bool_val)?
.this();When to box instead of copy
The pattern above copies Rust data into a fresh JS object. If you instead want to hand JavaScript an opaque handle to a live Rust value (and call back into it later), use boxed data with JsBox.