Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
945 views
in Technique[技术] by (71.8m points)

rust - Why can't I assign one dereference of a reference of a reference to another when the outer lifetimes differ?

I want to write the following function:

fn foo<'a, 'b, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
    *rr1 = *rr2;
}

But the compiler complains:

error[E0623]: lifetime mismatch
 --> src/lib.rs:2:12
  |
1 | fn foo<'a, 'b, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
  |                                 -----------       ------------------- these two types are declared with different lifetimes...
2 |     *rr1 = *rr2;
  |            ^^^^ ...but data from `rr2` flows into `rr1` here

My mental model of Rust's lifetimes does not agree that the code is wrong. I read the type of rr2 as "A reference with lifetime 'b to a reference with lifetime 'c to an u32". Thus when I dereference rr2, I get a reference with lifetime 'c to an u32. This should be safe to store in *rr1, which has the same type.

If I require that 'b outlives 'c, it works:

fn foo<'a, 'b: 'c, 'c>(rr1: &'a mut &'c mut u32, rr2: &'b mut &'c mut u32) {
    *rr1 = *rr2;
}

This makes me think that the type &'b mut &'c mut u32 means the u32 at the end of the reference chain is only available during the intersection of 'b and 'c.

What is the right explanation for Rust's behavior here? And why do references of references behave this way instead of the way I thought they do?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You cannot dereference a &'b mut &'c mut u32 and get a &'c mut u32 because:

  • &mut references are not trivially copiable, so you can't copy the &'c mut u32; and
  • You cannot move out of a reference, so you also can't move the &'c mut u32 (which would leave the outer reference dangling).

Instead, the compiler reborrows the u32 with the outer lifetime, 'b. This is why you get an error message that data from rr2 flows into rr1.

If foo were allowed to compile, you could use it to get two &mut references to the same u32, which is forbidden by the rules of references:

let (mut x, mut y) = (10, 20);
let mut rx = &mut x;
let mut ry = &mut y;
foo(&mut rx, &mut ry); // rx and ry now both refer to y
std::mem::swap(rx, ry); // undefined behavior!

If I require that 'b outlives 'c, it works

Because 'c must already outlive 'b1, if you require that 'b also outlives 'c, it follows that 'c = 'b. The updated signature is equivalent to this:

fn foo<'a, 'b>(rr1: &'a mut &'b mut u32, rr2: &'b mut &'b mut u32)

That is, you have unified 'c and 'b, and now there's no problem borrowing a &'b mut u32 from rr2 because the inner and outer lifetimes both live for 'b. However, the compiler now won't let you write the broken code in the example I gave earlier, since ry is already borrowed for its entire lifetime.

Interestingly, if you make the inner reference non-mut, it also works:

fn foo<'a, 'b, 'c>(rr1: &'a mut &'c u32, rr2: &'b mut &'c u32) {
    *rr1 = *rr2;
}

This is because & references are Copy, so *rr2 is not a reborrow, but actually just a copy of the inner value.

For more information, read:


1 It might not be obvious why 'c outlives 'b when there is no explicit 'c: 'b bound. The reason is because the compiler assumes that the type &'b mut &'c mut u32 is well-formed. Well-formedness can become complex (see RFC 1214) but in this case it just means you can't have a reference that's valid for longer ('b) than the thing it references ('c).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...