I am not sure why the following code does not compile.
use std::cmp::Ordering;
struct MyItr<\'a> {
cur: &\'a i32,
}
impl<\'a> Ord for M
There are two problems here. The first is that you've over-specified lifetimes, creating a situation the compiler just can't deal with.
fn f0<'a>(t0: &'a mut MyItr<'a>, t1: &'a mut MyItr<'a>, i: &'a i32)
You've told the compiler that all the arguments must be pointers with the same lifetime. The compiler can narrow overlapping lifetimes, but in this case that doesn't help. You've specified that the pointer to the MyItr
s has the same lifetime as the thing they point to, and the outer pointers are mutable.
The second problem is that (even after fixing that), what you're trying to do is just outright unsafe and will lead to dangling pointers.
Here's a more minimal example:
struct S<'a> {
ptr: &'a i32,
}
fn f<'b>(t: &'b mut S<'b>, new_ptr: &'b i32) {}
fn main() {
let i0 = 1;
let mut s = S { ptr: &i0 };
let i1 = 2;
f(&mut s, &i1);
}
What is 'b
? Well, the compiler can only narrow lifetimes, so usually you'd just take the lifetimes of everything you're trying to pass and pick the shortest one. In this case, that would be the lifetime of i1
. So, it has to narrow the lifetime of &s
. The lifetime on the pointer to s
itself isn't a problem (you can narrow how long you take a borrow for), but narrowing the inner lifetime (the one used for the ptr
field) is a problem.
If the compiler narrowed the lifetime of s.ptr
, you would then be able to store &i1
in that field. s
expects s.ptr
to outlive itself, but that will no longer be true: i1
will be destroyed before s
is, meaning s.ptr
will contain a dangling pointer. And Rust will not allow that to happen.
As a result, Rust can't narrow s
's inner 'a
lifetime... but if it can't narrow it, then that means 'b
must be the full, un-narrowed 'a
. But wait, that means that 'b
is longer than the lifetime of s
itself and i1
. And that's impossible.
Hence the failure.
The solution requires two things. First, you need to not over-specify lifetimes. Secondly, you need to ensure that some valid lifetime exists at all; in the case of your original code, that means moving i2
above z0
and z1
so that it outlives them. Like so:
fn f0<'a>(t0: &mut MyItr<'a>, t1: &mut MyItr<'a>, i: &'a i32) {
let t: &mut MyItr<'a> = std::cmp::max(t0, t1);
t.cur = i;
}
fn f1() {
let i0 = 1;
let i1 = 2;
let i2 = 3;
let mut z0 = MyItr { cur: &i0 };
let mut z1 = MyItr { cur: &i1 };
f0(&mut z0, &mut z1, &i2);
}
A rule of thumb: don't just spam a single lifetime everywhere. Only use the same lifetime for things that should be the same.