ralfbiedert / cheats.rs

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

Doubt in Memories & Lifetimes, subsection of References and Pointers #195

Closed mingmamma closed 5 months ago

mingmamma commented 5 months ago

Before I start, I like to echo the other issue to commend this awesome effort! I particularly like the item links that cross-reference to the original documentation at different detail levels (the Book, the std lib, Rust by example, Rustnomicon, Reference) such that it helps me continually dive deeper for better understanding from the right source.

I'd like to point out a place that caused me doubt and seek clarification. In the current section: https://cheats.rs/#memory-lifetimes, under the subsection References and Pointers, there is a code example snippet that reads as it currently stands:

let mut a = S(1);
let r = &mut a;
let d = r.clone();
*r = S(2); 

Filling in a few more code to make this a sensible example that is meant to compile & run, it would be

struct S(i32);
fn main() {
let mut a: S = S(1);
let r: &mut S = &mut a;
let d = r.clone(); // ?! This line doesn't compile on my end
*r = S(2); // this line wouldn't even be reached 
}

The reasoning for what I suggest is that, with the explicit type annotation added, we see variable r is of type &mut S. Clone trait and the clone() method, as far as I see, are usually not implemented for type &mut T, where T is some arbitrary type? Hence, that's what I think it doesn't compile. There seems to a few different ways to improve on this depending on what point we'd like this example to get across, but firstly I'd like to check if my understanding make sense

ralfbiedert commented 5 months ago

Thanks for the feedback!

The examples sometimes omit "unrelated" things, in this case the missing piece is #[derive(Clone)] on S (similar to how the function M::new() in 'Type Safety' isn't defined anywhere).

That said, I can see why this particular example is confusing, as you pointed out one could expect a hypothetical &mut S :: clone() to be invoked.

The main is how to best address this. I definitely want to address this (I remember I ran into similar issues when learning Rust), but I also don't want to be overly explicit just for the sake of it (e.g., I don't think defining M::new() would help clarify anything).

Thoughts?

mingmamma commented 5 months ago

OK, that would make good sense. I replicated the struct S which now implements the Clone trait and the accompanying code that compiles & runs

#[derive(Clone)]
struct S(i32);
fn main() {
    let mut a: S = S(1);
    let r: &mut S= &mut a;
    let d1 = r.clone();
    let d2 = (*r).clone();
    *r = S(2);
    println!("{}", a.0);
}

With this code snippet in mind (it's not necessary to update the original code snippet, but this one I have is just for clarifying our understanding, then the accompanying textual explanation would reads relatively more smoothly with some minor update:

in example above, suppose type S implements the Clone trait, clone d is created from r, and S(2) written to r. Method Clone::clone(&T) expects a reference itself, which is why we can use r as well as r. On assignment r = … old value in location also dropped (not shown above).

The minor updates I propose includes adding the precondition "suppose ..." that you clarified so that people like me would see the point and conclude with the right type the clone method works on in their mind in the first place. It also makes the statement of second line "Method Clone::clone(&T) ..." a bit more precise following the exact empirical observations

ralfbiedert commented 5 months ago

Just read that again, I agree it was poorly worded. I've added the line below and removed the confusing Clone::clone statement.

We assume S implements Clone, and r.clone() clones target-of-r, not r itself.

Thanks for the report!