1

I've got an exported-to-NodeJS Rust function that does something. Now, I want to extract part of that function into its own function (creating a JS object), so that I can reuse that logic in another, new exporter-to-NodeJS Rust function. However, I'm having great troubles figuring out how to pass the Context into that sub-function in a way that Rust is happy with. So far, the best (as in, "causes the compiler to complain the least") solution I have is this:

fn encode_num(mut cx: FunctionContext) -> JsResult<JsObject> {                                                                                                                                                                                 
    let input = cx.argument::<JsNumber>(0)?.value(&mut cx);                                                                                                                                                                                    
    let output = OrePlaintext::<u64>::from(input).0;                                                                                                                                                                                           
    plaintext_from_u64(&mut cx, output)                                                                                                                                                                                                        
}                                                                                                                                                                                                                                              

fn plaintext_from_u64<'a>(cx: &'a mut FunctionContext, n: u64) -> JsResult<'a, JsObject> {                                                                                                                                                     
    let bytes = n.to_ne_bytes();                                                                                                                                                                                                               
    let mut buf = match cx.buffer(8) {                                                                                                                                                                                                         
        Ok(b) => b,                                                                                                                                                                                                                            
        Err(e) => return cx.throw_error(format!("Failed to allocate buffer: {:?}", e))                                                                                                                                                         
    };                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                               
    cx.borrow_mut(&mut buf, |data| {                                                                                                                                                                                                           
        let slice = data.as_mut_slice::<u8>();                                                                                                                                                                                                 
        for i in 0..8 {                                                                                                                                                                                                                        
            slice[i] = bytes[i];                                                                                                                                                                                                               
        };                                                                                                                                                                                                                                     
    });                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                               
    let obj = cx.empty_object();                                                                                                                                                                                                               
    obj.set(&mut cx, "kind", cx.string("OrePlainText"))?;                                                                                                                                                                                      
    obj.set(&mut cx, "buf", buf)?;                                                                                                                                                                                                             
    Ok(obj)                                                                                                                                                                                                                                    
}

#[neon::main]                                                                                                                                                                                                                                  
fn main(mut cx: ModuleContext) -> NeonResult<()> {                                                                                                                                                                                             
    cx.export_function("encodeNumber", encode_num)?;                                                                                                                                                                                           
    Ok(())
}                                                                                                                                                                                                                                             

This fails to compile with the following errors:

error[E0277]: the trait bound `&'a mut CallContext<'_, neon::prelude::JsObject>: neon::context::Context<'_>` is not satisfied
   --> native/lib.rs:120:9
    |
120 |     obj.set(&mut cx, "kind", cx.string("OrePlainText"))?;
    |         ^^^ the trait `neon::context::Context<'_>` is not implemented for `&'a mut CallContext<'_, neon::prelude::JsObject>`
    |
    = help: the following implementations were found:
              <CallContext<'a, T> as neon::context::Context<'a>>

error[E0277]: the trait bound `&'a mut CallContext<'_, neon::prelude::JsObject>: neon::context::Context<'_>` is not satisfied
   --> native/lib.rs:121:9
    |
121 |     obj.set(&mut cx, "buf", buf)?;
    |         ^^^ the trait `neon::context::Context<'_>` is not implemented for `&'a mut CallContext<'_, neon::prelude::JsObject>`
    |
    = help: the following implementations were found:
              <CallContext<'a, T> as neon::context::Context<'a>>

I started with plaintext_from_u64 just taking the context as mut cx: FunctionContext, which is how encode_num accepts it, but that then causes problems as soon as I want to call plaintext_from_u64 more than once, because "value borrowed after move", and if I try to borrow in the calls, I get "expected struct CallContext, found mutable reference. I've dug around in the neon examples repo, looking for exported functions that pass a context into an internal function, but I haven't been able to find anything so far, which is... frustrating.

What is the compiler-approved approach to this problem?

womble
  • 12,033
  • 5
  • 52
  • 66
  • What was your original function before trying to split out this part? – PitaJ Feb 28 '22 at 18:15
  • I think your problem is that because `cx` is already an `&mut FunctionContext`, when you pass it into `obj.set` as `&mut cx`, you're passing in an `&mut &mut FunctionContext`. Try passing it without the `&mut` like `obj.set(cx, "buf", buf)?;`. – PitaJ Feb 28 '22 at 18:24
  • Here's a minimal example on the [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=63e3d321d6418822e880c21652fb73e4). Interestingly, it provides a more helpful hint: "help: consider removing the leading `&`-reference". What version of the compiler are you using? – PitaJ Feb 28 '22 at 18:37

0 Answers0