Closed wbelhomsi closed 6 months ago
@wbelhomsi Great question! There are several different options, and which one works best for is going to depend on your needs.
OnceLock
This is the simplest option and is standard Rust. Store the variable in a global that everything can access. This is a good option if you want a singleton for the entire process (e.g., tokio::runtime::Runtime
).
// Create a globally shared `String`
fn global() -> &'static str {
use std::sync::OnceLock;
static GLOBAL: OnceLock<String> = OnceLock::new();
GLOBAL.get_or_init(|| String::from("Hello, World!"))
}
LocalKey
This is very similar to OnceLock
, but instead of a single global per process, there is one per instance of the JavaScript VM. This is useful if you need a distinct global for each time the module is loaded in a web worker. For example, if you are storing callbacks in a Root<JsFunction>
.
This is very similar to LocalKey
. You can create a JsFunction
from a closure, so it's always possible to create a shared value and move clones of it into function.
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
let msg = Arc::new(String::from("Hello, World!"));
cx.export_function(&mut cx, {
let msg = msg.clone();
move |mut cx| Ok(cx.string(&msg))
});
}
JsBox
JsBox
is a type that lets you wrap a Rust struct with a JavaScript object for later access. This lets the JS garbage collector track and free it when needed. To use it, you can store a reference to it in JavaScript and pass it as an argument to your functions.
This works well when you are trying to implement a class or need multiple copies for some other reason. See this example.
Hey @kjvalencik, thanks for your answer!
I did the following:
// Create a globally shared `XClient`
fn global() -> &'static XClient {
use std::sync::OnceLock;
static GLOBAL: OnceLock<XClient> =
OnceLock::new();
let xclient_config = XConfig::default();
let x_client = XClient::new("http://xxxx", xclient_config).unwrap();
GLOBAL.get_or_init(|| x_client)
}
In my main I did:
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
println!("Holllaaa");
global(); // To initialize it
cx.export_function("get", get_num_cpus)?;
cx.export_function("ping", ping)?;
Ok(())
}
and now I'm using it as the following:
fn ping(mut cx: FunctionContext) -> JsResult<JsObject> {
let x_client_ref = global();
...
}
Just wanted to validate I understood what you told me.
@wbelhomsi The initialization lines should be inside the closure for get_or_init
, otherwise they are going to run each time global()
is called.
// Create a globally shared `XClient`
fn global() -> &'static XClient {
use std::sync::OnceLock;
static GLOBAL: OnceLock<XClient> =
OnceLock::new();
GLOBAL.get_or_init(|| {
let xclient_config = XConfig::default();
XClient::new("http://xxxx", xclient_config).unwrap()
})
}
@kjvalencik I knew it, was so close but had a ;
at the end of the unwrap()
which made it not happy.
My gratitude. Thanks for taking the time to help a fellow dev
Hi! I'm trying to get a bit more into rust and I am using neon as a first step to get there. My main expertise lies within JS/TS, so forgive me if I'm asking something basic within Rust. I'm essentially trying to have the following; I'd like to use a variable that I have defined within
main
inside of the functions that will be called within Javascript so that I don't have to initialize them every time I call that function.The below is a dummy example of what I'm trying to do
In Js something like that would be done like:
Apologies if I'm missing something essential from RUST