Variables have different lifespan when they should have the same

To learn Rust, I write a library wrapping JNI. I ran into many life problems and bravely fought with the compiler to solve them, but with this I just give up. Here is the problem.

Now JNI is the interface of a Java virtual machine, so each Java object must be bound to a virtual machine. To do this, I created a "Java pointer type VM" JavaVM, which basically is a wrapper for the pointer and an interface for creating JavaEnv objects, which are only wrappers for JNIEnv *, as well as providers of a more secure interface for most JNI methods.

To declare that the JavaEnv object is bound to a virtual machine, I did the following:

pub struct JavaEnv<'a> {
    phantom: PhantomData<&'a JavaVM>,
    ...
}

Now, if I understand correctly, all JavaEnv objects will be bound to some JavaVM object in terms of lifetime and will not survive it, which is exactly what I want.

JavaEnv is an interface for manipulating Java objects (and some others). Now all types of JNI objects implement the attribute:

pub trait JObject<'a>: Drop {
    fn get_env(&self) -> &'a JavaEnv<'a>;
    ...
}

and they all look like this:

pub struct JavaObject<'a> {
    env: &'a JavaEnv<'a>,
    ...
}

pub struct JavaClass<'a> {
    env: &'a JavaEnv<'a>,
    ...
}

Now, if I understand correctly, all JavaObjects are bound to some JavaEnv object with a lifetime, which, in turn, is bound to a JavaVM object.

Finally, Java is a language that uses reference semantics by default, so comparing objects is just a superficial comparison of links, and I wanted to reflect it in the Rust interface:

impl<'a, R: 'a + JObject<'a>> PartialEq<R> for JavaObject<'a> {
    fn eq(&self, other: &R) -> bool {
        self.get_env().is_same_object(self, other)
    }
}

impl<'a, R: 'a + JObject<'a>> PartialEq<R> for JavaClass<'a> {
    fn eq(&self, other: &R) -> bool {
        self.get_env().is_same_object(self, other)
    }
}

pub fn JavaEnv::is_same_object<T1: 'a + JObject<'a>, T2: 'a + JObject<'a>>(&self, obj1: &T1, obj2: &T2) -> bool {
    unsafe {
        ((**self.ptr).IsSameObject)(self.ptr, obj1.get_obj(), obj2.get_obj()) == JNI_TRUE
    }
}

This does not work. Here's the test:

let (cls, cap) = JavaClass::find(&env, "java/lang/String", cap).unwrap();
let (obj /*of class java/lang/String*/, cap) = cls.alloc(cap).unwrap();
let cls1 /*also java/lang/String*/ = obj.get_class(&cap);
assert!(cls1 == cls); 
let (sobj /*also of java/lang/String*/, cap) = JavaString::new(&env, "hi!", cap).unwrap();
assert!(cls1 != sobj);
let scls /*also java/lang/String*/ = sobj.get_class(&cap);
assert!(scls == cls1);
assert!(scls == cls);
// TODO: somehow these cls1, scls and cls have different lifetimes (or not?)
// So those two asserts do not compile!!
assert!(cls1 == scls);
assert!(cls == scls);

To “fix”, I had to change the eq code:

impl<'a, 'b, R: 'b + JObject<'b>> PartialEq<R> for JavaObject<'a> {
    fn eq(&self, other: &R) -> bool {
        self.get_env().is_same_object(self, other)
    }
}

impl<'a, 'b, R: 'b + JObject<'b>> PartialEq<R> for JavaClass<'a> {
    fn eq(&self, other: &R) -> bool {
        self.get_env().is_same_object(self, other)
    }
}

pub fn JavaEnv::is_same_object<'b, T1: 'a + JObject<'a>, T2: 'b + JObject<'b>>(&self, obj1: &T1, obj2: &T2) -> bool {
    unsafe {
        ((**self.ptr).IsSameObject)(self.ptr, obj1.get_obj(), obj2.get_obj()) == JNI_TRUE
    }
}

JObjects, ! : , ? JavaEnv , , JavaVM!

, : , ?

+4
1

, . :

fn are_equal<'a>(a: &'a u8, b: &'a u8) -> bool {
    *a == *b
}

fn main() {
    let a = 42;

    {
        let b = 84;
        println!("{}", are_equal(&a, &b));
    }
}

, lifetime 'a. , a b ( main) - a b - .

, , - , , , - . , .

+2

All Articles