Newbie question regarding "follow the pointer"

I've some of the concepts of references, and that it solves multiple living-refences that would otherwise lead to double free, or to access de-allocated data.

In the case below, one isn't using references but just "following the pointers" with *. I'm aware that "pointer" isn't the best concept maybe, but idk what to use here.

I think the reason why the first one is not allowed is what I stated above (but may be wrong?)

However, I don't get why wouldn't the Box<T> behave the same. Instead it moves the data, or copies it if it's a number.

Is there a simple explanation? (up to beginning of chapter 5 this wasn't yet explained in the book, imho.)

fn main(){
    dereferencing_cases();
}
fn follow_the_pointer_cases() {
    let v = vec![String::from("meow")];
    let y = *v[0]; // Does not or it could cause troubles (maybe double frees)
    // or access data that has moved i.e v2[0]

    let ss = Box::new(String::from("My String"));
    let ss2 = *ss; // works fine, and moves the String!
    println!("{ss2}")
    // so println!("{ss:?}") would fail
}

(tbc: i'm aware we can just clone it as an option.)

It's a limitation of index expressions, which are place expressions that don't allow moving out of. You have to use a method of the collection instead, like Vec::remove, or VecDeque::pop_front for example.

1 Like

Box is special; if x: Box<Something> then you can move a value out of *x, but it is the only type that supports this ability to move out of a dereference. The fact that Box is unique in this way is largely a historical accident — Box used to be even more magical than it is today, and no one has yet invented a solid plan for extending this to arbitrary other types.

3 Likes

Unfortunately this is the wrong explanation.

v[0] already "follows" one pointer and refers to the memory location where the String. You're then trying to follow the pointer that the String represents (because it implements the Deref trait). However String points to a str, which is an "unsized" type, because a str could have any length, and so you can't follow it and move/copy it to a variable, because variable can only contain "sized" data, that is data whose type's size is known at compile time.

I encourage you to try the same but storing a Box<String> and/or Box<i32> in the Vec and seeing what happens.

1 Like

Thanks for the correction @SkiFire13. But one could still use *v right? Or even v[0] which does not move the String either.

And this does still fail:

    let v = vec![Box::new(String::from("meow"))];
    let y = *v[0];

Due to the Copy trait, but does not fail in the Box(String).

You can not move elements out of a vector. The actual element type does not matter, moving an element out of a vector would leave a void hole, making the whole vector invalid. Think of a vector with n elements, moving an element with index < n out. There would be a hole.

2 Likes

Yes, that makes sense, what doesn't to me, in a way, is that one can remove it (move it) from a Box.

Thanks @kpreid, that's interesting.

So ref/deref is the right term even though it allows moving from Box. (normally one can not move data behind refs iiuc.) Just Box is different.

From the links by @jofas, which I may misunderstand, I wonder whether this is bc Box implements "Sized" and Vecs don't ?

Or maybe it was simply this sentence it was being pointed at:

  • The result of dereferencing an expression with type Box<T> and that can also be moved out of.

So Vectors' place expressions do not support moving data out.

Vec is also Sized. Your *v[0] dereferences the String in v[0] into a str, which is !Sized.

Yes.

1 Like