How to initialize fields of structure correctly?

Hi everyone,
I'm trying to initialize structure:

use jni::{AttachGuard, InitArgsBuilder, JNIVersion, JavaVM};

pub struct JEnv<'a> {
    jvm: JavaVM,
    pub jenv: AttachGuard<'a>,
}

impl<'a> JEnv<'a> {
    fn new(class_path: Vec<String>) -> Self {
        let jvm_args = InitArgsBuilder::new()
            .version(JNIVersion::V8)
            .option("-Xcheck:jni")
            .option(format!("-Djava.class.path={}", class_path.join(":")))
            .build()
            .unwrap();

        let ljvm = JavaVM::new(jvm_args).unwrap();
        let mut ljenv = ljvm.attach_current_thread().unwrap();

        Self {
            jvm: ljvm,
            jenv: ljenv,
        }
    }
}

but got compilation failed:

  --> rsjrs/src/jenv.rs:18:13
   |
18 |         let mut ljenv = ljvm.attach_current_thread().unwrap();
   |             ----^^^^^
   |             |
   |             help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

error[E0515]: cannot return value referencing local variable `ljvm`
  --> rsjrs/src/jenv.rs:20:9
   |
18 |           let mut ljenv = ljvm.attach_current_thread().unwrap();
   |                           ---- `ljvm` is borrowed here
19 |
20 | /         Self {
21 | |             jvm: ljvm,
22 | |             jenv: ljenv,
23 | |         }
   | |_________^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `ljvm` because it is borrowed
  --> rsjrs/src/jenv.rs:21:18
   |
8  |   impl<'a> JEnv<'a> {
   |        -- lifetime `'a` defined here
...
17 |           let ljvm = JavaVM::new(jvm_args).unwrap();
   |               ---- binding `ljvm` declared here
18 |           let mut ljenv = ljvm.attach_current_thread().unwrap();
   |                           ---- borrow of `ljvm` occurs here
19 |
20 | /         Self {
21 | |             jvm: ljvm,
   | |                  ^^^^ move out of `ljvm` occurs here
22 | |             jenv: ljenv,
23 | |         }
   | |_________- returning this value requires that `ljvm` is borrowed for `'a`

How can it be fixed? (I'm a novice in Rust)

You're trying to make a self-referential struct - this is essentially impossible to do in safe Rust (and very hard to do correctly with unsafe). You probably want to create JavaVM somewhere outside JEnv::new and pass &ljvm as argument to new.

1 Like

You probably want to create JavaVM somewhere outside JEnv::new and pass &ljvm as argument to new .

actually I would like to hide everything related to jni:: inside this structure and leave only simple high-level API functions for end-point user.

In this case, can you create ljenv on demand, not storing it in JEnv? Or are they too expensive to build?

1 Like

You'll have to use two separate structs, each wrapping one of the fields, and pass them as separate params, in order to avoid a single self-referential struct. The wrapper structs will still "hide" the internals.

yeah, at the moment I've done it in the same way
but I would to try to minimize redundant abstractions

maybe it can be possible to implement smth like that with smart pointers?

One struct is simpler than two, in one sense. But in another sense using two structs will define the meaning of the structs more clearly. And in any case, you are constrained by the rules of Rusts references, and this is often an overriding factor.