napi-rs icon indicating copy to clipboard operation
napi-rs copied to clipboard

pass object/class as parameter

Open kratess opened this issue 1 year ago • 3 comments

How can you create a function like this in js?

function a(res) {
  res.setA(42);
}

The only work around i foun was to create a type in rust like this:

#[napi] pub type Test = fn(res: &mut Response);

but has a problem #[napi] can only be applied to a function, struct, enum, const, mod or impl.

What should I do?

kratess avatar Jul 07 '24 00:07 kratess

See https://github.com/napi-rs/napi-rs/blob/main/examples/napi/src/function.rs for example

Brooooooklyn avatar Jul 08 '24 07:07 Brooooooklyn

See https://github.com/napi-rs/napi-rs/blob/main/examples/napi/src/function.rs for example

I tried something following the link provided:

#[derive(Debug, Clone, Copy)]
#[napi]
pub struct Res {
  a: i32,
}

#[napi]
impl Res {
  #[napi(constructor)]
  pub fn new() -> Res {
    Res { a: 0 }
  }

  #[napi]
  pub fn set_a(&mut self, a: i32) {
    self.a = a;
  }
}

#[napi]
pub fn call(path: String, callback: Function<Rc<Res>, ()>) -> () {
  let response: Res = Res::new();

  let x = Rc::new(response);
  let y = Rc::clone(&x);

  println!("RUST#1 {:?}", response);

  callback.call(x).unwrap();

  println!("RUST#2 {:?}", y);
}

I need to pass the object as a parameter but then after all the function has done its things the parameter should be again return to rust code.

I also tried using Function<Rc<RefCell<Res>>, ()>) but the trait bound Rc<RefCell<Res>>: ToNapiValue is not satisfied

Another weird fact is the console.log visualization of the object.

JS code:

const { call } = require('./index')

call("/path", (res) => {
  res.setA(66);
  console.log('res1:', res);

  res.a = 55;
  console.log('res2:', res);
})

Here is all the logs:

RUST#1 Res { a: 0 }
res1: Res {}
res2: Res { a: 55 }
RUST#2 Res { a: 0 }

Why on the first console.log res1 the log does not include the a value after being set with the method setA? But on the second log explicitly setting with res.a = 55; it does appear in the log?

I also tried all methods in the linked file. Some of witch neither worked env.spawn_future_with_callback no method named spawn_future_with_callback found for struct Env and callback.build_threadsafe_function() no method named build_threadsafe_function found for struct napi::bindgen_prelude::Function

kratess avatar Jul 08 '24 14:07 kratess

Passing Rc<T> from rust side to js side is actually passing a cloned value. So any mutation on one side will not affect the other. A simple solution is to pass a napi object to js side and pass the mutated one back.

#[derive(Debug)]
#[napi(object)]
pub struct Res {
  pub a: i32,
}

#[napi]
pub fn call(path: String, callback: Function<Res, Res>) -> () {
  let response = Res { a: 0 };

  println!("RUST#1 {:?}", response);

  let response = callback.call(response).unwrap();

  println!("RUST#2 {:?}", response);
}
const { call } = require("./index");

call("/path", (res) => {
  console.log("res:", res);
  res.a = 55;
  console.log("res:", res);
  return res;
});

results:

RUST#1 Res { a: 0 }
res: { a: 0 }
res: { a: 55 }
RUST#2 Res { a: 55 }

CPunisher avatar Aug 13 '24 06:08 CPunisher