Reputation: 390
To learn Rust, I am writing a library wrapping JNI. I have encountered lots of lifetime issues and fought the compiler bravely to resolve them, but with this one I just give up. Here's the problem.
Now, JNI is the interface to Java virtual machine, so every java object has to be tied to the VM. In order to do that, I have created th "VM pointer" type JavaVM, which is mostly just a wrapper for the pointer and an interface for creating JavaEnv objects, which are just wrappers for JNIEnv*, and also providers of a safer interface for most of JNI methods.
In order to declare that JavaEnv object is tied to the VM I did the following:
pub struct JavaEnv<'a> {
phantom: PhantomData<&'a JavaVM>,
...
}
Now, if I understand correctly, all JavaEnv objects will be tied to some JavaVM object by the lifetime, and will not outlive it, which is exactly what I want.
JavaEnv -- is an interface for manipulating java objects (and some other stuff). Now, all types of JNI objects implement a trait:
pub trait JObject<'a>: Drop {
fn get_env(&self) -> &'a JavaEnv<'a>;
...
}
and themselves all look like this:
pub struct JavaObject<'a> {
env: &'a JavaEnv<'a>,
...
}
pub struct JavaClass<'a> {
env: &'a JavaEnv<'a>,
...
}
Now, if again I understand correctly, all JavaObject-s will be tied to some JavaEnv object by the lifetime, which in turn is tied to a JavaVM object.
Finally, Java is the language with default reference semantics, so object comparison is just a shallow reference comparison 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 doesn't work. Here's a 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" this 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
}
}
But I DON'T want to compare JObjects attached to different VM's! And, even more: I just don't get, how those objects ended up having different lifetime parameters? They all were obtained from the same JavaEnv and hence the same JavaVM!
So, the questions are: what did I do wrong, why this is happening and how to fix this?
Upvotes: 4
Views: 189
Reputation: 432199
I believe your basic premise has an issue. Check out this example code:
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));
}
}
Here, we have a method that takes two reference arguments with the lifetime parameter 'a
. However, you can clearly see that the actual lifetimes of a
and b
(in main
) are not the same — a
outlives b
due to the block ending.
All that is required is that there is some lifetime that can unify both parameters, and there is - the scope of the inner block. It doesn't mean that the entire lifetime has to be exactly equal.
Upvotes: 2