Hi kpreid,
first of all, many thanks for your super quick answer!
Mutable references are invariant over their referent type. Because it is possible to write to a mutable reference, anything written to it must be valid for exactly as long as originally specified. In your program, x
must not contain an &i32
that is shorter-lived than its actual scope. But, with the immutable reference, it's perfectly fine for foo
to receive an &&a
that, as far as it knows, refers to something that was just created for its sake — immutable references are covariant over their reference type.
(Note that the lifetime being shortened or not is not the one belonging to the reference itself, but its contents.)
Ok, so do I get it right that due to covariance, for the code with immutable references,
fn foo<'a, 'b> (_x : & &'a1 i32, _y : &'b i32) where 'b : 'a1
{}
fn main() {
let x : &'a0 i32 = &0; /* invalid syntax, for demo purposes */
{
let y : i32 = 1;
foo(&x, &y);
}
println!("{}", x);
}
the compiler is free to temporarily create a "supertype" & &'a1 i32
out of x'
original & &'a0 i32
, with 'a0 : 'a1
, just to satisfy the foo()
constraints? If so, it's starting to make some sense now.
But note that in the original code, the type of x
is more like &MyWrapper<i32>
, I turned it into & & i32
here for illustration purposes and to have a minimal example.
I think we will need to see something closer to your real code to see what the problem is. I've described the difference between &
and &mut
behavior, but that only affects lifetimes, so it shouldn't do anything if MyWrapper
or its contained type doesn't have a lifetime parameter.
The MyWrapper
does actually have a lifetime parameter, but I think the code might suffer from the same issue I've been seeing with &&'a i32
.
use core::cell::Cell;
use core::ops::Deref;
pub struct MyWrapper<'a, T> {
ptr : Cell<*const T>,
_phantom : PhantomData<&'a T>,
}
impl<'a, T> MyWrapper<'a, T> {
pub const fn new(r : &'a T) -> Self {
Self {
ptr : Cell::new(r as *const T),
_phantom : PhantomData,
}
}
pub fn set<'b>(&self, r : &'b T) where 'b : 'a {
unsafe { self.ptr.as_ptr().write(r as *const T) };
}
}
impl<'a, T> Deref for MyWrapper<'a, T> {
type Target = T;
fn deref(&self) -> &'a T {
unsafe { &*self.ptr.get() }
}
}
// Compiler complains as expected:
fn foo<'a, 'b> (x : &mut MyWrapper<'a, i32>, y : &'b i32) where 'b : 'a
{
x.set(y);
}
fn f() {
let mut x : MyWrapper<'_, i32> = MyWrapper::new(&0);
{
let y : i32 = 1;
foo(&mut x, &y);
}
println!("{}", *x);
}
// Does not complain as I would have naively expected:
fn bar<'a, 'b> (x : &MyWrapper<'a, i32>, y : &'b i32) where 'b : 'a
{
x.set(y);
}
fn g() {
let x : MyWrapper<'_, i32> = MyWrapper::new(&0);
{
let y : i32 = 1;
bar(&x, &y);
}
println!("{}", *x);
}