ralfbiedert / cheats.rs

Rust Language Cheat Sheet - https://cheats.rs
https://cheats.rs
4.17k stars 397 forks source link

"References to References" section has lifetime (compilation) error in the example code. #202

Closed sswybd closed 10 hours ago

sswybd commented 11 hours ago

In https://cheats.rs/#memory-lifetimes's "References to References" section, there is the sample code like this:

let mut ra: &'a mut S = …;

let rval = f1sr(&&*ra);
// Some fictitious place where we use `ra` and `rval`, both alive.
compute(ra, rval);

I wrote the following code, which is basically the same and directly runnable:

#![allow(unused)]

fn main() {
    let mut ra= &mut S(5);
    let rval = f1lr(&&*ra);
    compute(ra, rval);
}

#[derive(Default, Debug)]
struct S(i32);

fn f1lr<'b, 'a>(rb: &'b &'a S) -> &'a S { *rb }

fn compute(ra: &mut S, rval: &S) {
    std::mem::take(ra);
}

This code is invalid since the reborrow of rval's lifetime is interrupted by the mutable borrow ra. The sample code here is therefore incorrect.

sswybd commented 10 hours ago

And for the f1sr function, even if we dealt with the temporary value dropped issue in the following code, the same error as above would still be imminent.

#![allow(unused)]

fn main() {
    let mut ra= &mut S(5);
    let binding = &*ra;
    let rval = f1sr(&binding);
    compute(ra, rval);
}

#[derive(Default, Debug)]
struct S(i32);

fn f1sr<'b, 'a>(rb: &'b &'a S) -> &'b S { *rb }

fn compute(ra: &mut S, rval: &S) {
    std::mem::take(ra);
}
ralfbiedert commented 10 hours ago

I think you're confusing the purpose of the example, but I can see the last mention of compute might be misleading.

The purpose of the example is to show what's possible inside the f functions, and f1sr works as shown. The reason you are running into a compilation error is because your compute takes ra: &mut S, instead of &S.

You can't have a mutable and a shared borrow to the same thing at the same time, so in a sense all / most of the examples were broken if you wanted to have a compute that took both.

sswybd commented 9 hours ago

So does this mean that the example usages are only "examples", not that strict and should be slightly modified for real cases (for compilation success) sometimes. The f1sr, f1lr, etc. themselves are the things that matter, not the latter usage of them?

sswybd commented 9 hours ago

And this modification also includes we should for example add let binding = &*ra; to then use let rval = f1sr(&binding);.

ralfbiedert commented 8 hours ago

So does this mean that the example usages are only "examples", not that strict and should be slightly modified for real cases (for compilation success) sometimes

That's contextual, but generally yes.

You are right that practically none of the code snippets are self-contained, so you can't just randomly copy-paste them and expect them to work "as-is" (we're a cheat sheet after all, not Rust by Example).

That said, all snippets should be correct and work in the context they are portrayed in (if not, that's a bug) and the expectation is we make it clear what that context is.

In this particular case the footnote

f_lr cases can fail because returning &'a S from &'a mut S to caller means there would now exist two references (one mutable) to same S which is illegal

even touches the problem of the overlapping borrow issue you ran into.

The f1sr, f1lr, etc. themselves are the things that matter, not the latter usage of them?

In this case, yes.

And this modification also includes we should for example add let binding = &*ra; to then use let rval = f1sr(&binding);.

This should not be necessary, but that depends a bit on the code you are coming from. While Rust respects what you declare explicitly inside signatures, what happens inside function bodies is a different story.

Rust might sometimes apply "compiler magic" to fix references (e.g., deref, coercions, shorten lifetimes) for you, so that doesn't have a simple answer, has even changed between editions, and is a known 'issue' that makes using the language easy, but learning it hard.

I recommend taking some time to read Language Sugar.

All that said, we should probably add a footnote regarding compute to make it clear it takes two ordinary references.

sswybd commented 8 hours ago

This answers all my questions. Thanks a lot!