Skip to content

Commit

Permalink
Address issues found in KJ's review
Browse files Browse the repository at this point in the history
  • Loading branch information
dherman committed Sep 11, 2021
1 parent 526c664 commit bdbfbfa
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 11 deletions.
10 changes: 6 additions & 4 deletions docs/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ Neon's main way of connecting Rust and JavaScript is by allowing you to define *

## Defining Functions

A Neon function looks and acts to JavaScript like a regular JavaScript function, but its behavior is defined in pure Rust. Defining a Neon function requires first defining a Rust function of type `fn(FunctionContext) -> JsResult<T>` where `T` can be any type that implements the [`Value`](https://docs.rs/neon/latest/neon/types/trait.Value.html) trait:
A Neon function looks and acts to JavaScript like a regular JavaScript function, but its behavior is written in Rust. Creating a Neon function requires first defining a Rust function of type `fn(FunctionContext) -> JsResult<T>` where `T` can be any type that implements the [`Value`](https://docs.rs/neon/latest/neon/types/trait.Value.html) trait:

```rust
fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
Ok(cx.string("hello"))
}
```

The `cx` argument is a [`FunctionContext`](https://docs.rs/neon/latest/neon/context/type.FunctionContext.html), which provides the Neon function with access to the JavaScript runtime. The [`JsResult`](https://docs.rs/neon/latest/neon/result/type.JsResult.html) result type indicates that a Neon function may throw a JavaScript exception. In this simple example, we just construct a string and return it, so we immediately wrap the outcome in an `Ok` result.
The `cx` argument is a [`FunctionContext`](https://docs.rs/neon/latest/neon/context/type.FunctionContext.html), which provides the Neon function with access to the JavaScript runtime. The [`JsResult`](https://docs.rs/neon/latest/neon/result/type.JsResult.html) result type indicates that a Neon function may throw a JavaScript error. In this example, we just construct a string and return it, so we immediately wrap the outcome in an `Ok` result. A more involved Neon function might call other functions or interact with objects in ways that could end up triggering errors.

The most common way to define a Neon function from `hello` is to export it from our module using [`ModuleContext::export_function()`](https://docs.rs/neon/latest/neon/context/struct.ModuleContext.html#method.export_function):

Expand All @@ -28,6 +28,8 @@ pub fn main(mut cx: ModuleContext) -> NeonResult<()> {
}
```

Notice that the `main` function also returns a result, because it can trigger JavaScript errors. In this code, calling the `export_function()` method could potentially throw an error since it's interacting with the module object. We use Rust's [`?` operator](https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator) to return early and propagate any errors that get thrown.

A JavaScript module can then call the `hello` function by importing from the Neon module:

```javascript
Expand Down Expand Up @@ -126,7 +128,7 @@ You can call a JavaScript function from Rust with [`JsFunction::call()`](https:/

```rust
let global = cx.global();
let func: Handle<JsFunction> = global.get(&mut cx, "parseInt")?;
let func = global.get(&mut cx, "parseInt")?.downcast_or_throw::<JsFunction>()?;

This comment has been minimized.

Copy link
@kjvalencik

kjvalencik Sep 11, 2021

Member
let func = global.get(&mut cx, "parseInt")?.downcast_or_throw::<JsFunction, _>(&mut cx)?;
let null = cx.null();
let s = cx.string(s);
let result = func.call(&mut cx, null, vec![s])?;
Expand All @@ -140,7 +142,7 @@ You can call a JavaScript function as a constructor, as if with the [`new`](http

```rust
let global = cx.global();
let ctor: Handle<JsFunction> = global.get(&mut cx, "URL")?;
let ctor = global.get(&mut cx, "URL")?.downcast_or_throw::<JsFunction>()?;
let url: Handle<JsString> = cx.string("https://neon-bindings.com");
let result = ctor.construct(&mut cx, vec![url]);
```
4 changes: 2 additions & 2 deletions docs/hello-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ sidebar_label: Hello World!

**Full source code:** [examples/cpu-count](https://github.com/neon-bindings/examples/tree/main/examples/cpu-count)

This simple example project will be a tiny module that returns the number of processors in the current device. If you're not familiar with fancy systems concepts like processors and CPUs, don't panic! We'll be using the [`num_cpus`](https://crates.io/crates/num_cpus) crate to do all the heavy lifting for us, and we'll just return the number it gives us.
This small example project will be a module that returns the number of processors in the current device. If you're not familiar with fancy systems concepts like processors and CPUs, don't panic! We'll be using the [`num_cpus`](https://crates.io/crates/num_cpus) crate to do all the heavy lifting for us, and we'll just return the number it gives us.

Even this simple project demonstrates some of Neon's power: Rust's [crate ecosystem](https://crates.io/) is younger than npm but growing quickly and full of many 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](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) pool in an [Electron](https://electronjs.org) app.
The tutorial is short, but it demonstrates some of Neon's power: Rust's [crate ecosystem](https://crates.io/) is growing quickly and full of many useful and unique libraries, often providing low-level capabilities or high-performance data structures that can be hard to find in npm. A library like `num_cpus` could be useful, for example, as a hint for tuning the size of a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) pool in an [Electron](https://electronjs.org) app.

# Creating a New Project

Expand Down
46 changes: 41 additions & 5 deletions docs/objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,34 @@ Here is a simple example of converting a Rust `struct` to a JavaScript object. F
struct Book {
pub title: String,
pub author: String,
pub year: u64,
pub year: u32,
}
```

To copy a `Book` into a JavaScript object, we'll define a conversion function. Just for fun and to make it a little more idiomatically Rusty, we'll define it as a method of the `Book` type:
To copy a `Book` into a JavaScript object, we'll define a conversion function. To make it idiomatically Rusty, let's define it as a [_method_](https://doc.rust-lang.org/book/ch05-03-method-syntax.html) of the `Book` type, so that callers of our API can use a pleasant method call syntax:

```rust
let obj = book.to_object(&mut cx)?;
```

First let's look at the signature of `Book::to_object()`, which we define as a method using Rust's `impl Book` syntax and a `&self` parameter:

```rust
impl Book {
fn to_object(&self, mut cx: FunctionContext) -> JsResult<JsObject> {
fn to_object<'a>(&self, cx: &mut FunctionContext<'a>) -> JsResult<'a, JsObject> {
// ...
}
}
```

This is our first example using a _[lifetime annotation](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html)_ `'a`. This allows the Rust compiler to ensure that our code never accidentally makes an unsafe reference to JavaScript values managed by the Node runtime. Specifically, this signature tells Neon that the result object returned by this function (which has lifetime `'a`) is managed by the runtime context that was passed in as an argument (which also has that same lifetime `'a`).

If you've never seen lifetimes before or are not yet confident using them, don't worry! For now, you can use this code as a template, and know that the Rust compiler will keep you safe.

Now here is the full implementation:

```rust
fn to_object<'a>(&self, cx: &mut FunctionContext<'a>) -> JsResult<'a, JsObject> {
let obj = cx.empty_object();

let title = cx.string(self.title);
Expand All @@ -74,14 +93,31 @@ impl Book {
}
```

Let's walk through the implementation a step at a time. The `to_object` method takes a reference to `self` and a runtime context. Next, it constructs a new empty JavaScript object, which will serve as the result, converts each of the fields to a [primitive type](primitive-types) and sets the relevant property on the object to its value. Finally, the method returns the new object, wrapped in an `Ok` value to signal success.
Let's walk through the implementation. First, it constructs a new empty JavaScript object, which will serve as the result, converts each of the fields to a [primitive type](primitive-types) and sets the relevant property on the object to its value. Finally, the method returns the new object, wrapped in an `Ok` value to signal success.

One thing worth noticing about this function is that it doesn't use anything specific about the `FunctionContext` type other than the generic methods of the [`Context`](https://docs.rs/neon/latest/neon/context/trait.Context.html) trait. To make our function even more powerful, we can make it _generic_ and accept any implementation of `Context`:

```rust
impl Book {
fn to_object(&self, mut cx: impl Context) -> JsResult<JsObject> {
fn to_object<'a>(&self, cx: &mut impl Context<'a>) -> JsResult<'a, JsObject> {
// same as before...
}
}
```

This allows us to use our method in more places, such as with a [`ModuleContext`](https://docs.rs/neon/latest/neon/context/struct.ModuleContext.html):

```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(())
}
```
2 changes: 2 additions & 0 deletions docs/primitive-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ let size: usize = std::mem::size_of::<u128>();
let n = cx.number(size as f64)
```

Keep in mind that for some types, explicit casts to `f64` might be lossy, so you'll want to check whether the conversion you're doing is appropriate. Another option is to use the [`TryFrom`](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) API, which can help avoid loss of precision.

## Strings

The [`Context::string()`](https://docs.rs/neon/latest/neon/context/trait.Context.html#method.string) method constructs a JavaScript string from a reference to a Rust string.
Expand Down

0 comments on commit bdbfbfa

Please sign in to comment.