1

The following Rust code is from the book Programming Rust. I inserted some (hopefully correct?) pointer addresses to be printed in order to check where the objects reside in memory.

fn main() {
    struct Person {
        name: Option<String>,
        birth: i32,
    }
    let mut composers = Vec::new();
    composers.push(Person {
        name: Some("Palestrina".to_string()),
        birth: 1525,
    });
    println!("first_elem addr {:p}", &composers[0].name);
    let first_name = std::mem::replace(&mut composers[0].name, None);
    println!("first_elem addr {:p}", &composers[0].name);
    println!("first name addr {:p}", &first_name);
}

Note that the last displayed object address is totally different from the original location where the value Some("Palestrina".to_string()) resided. Given that is true: I think it would be fair to say that a new object was created (whose value equals the former name of composers[0], but it is still in a fresh memory location).

However the terminology that is used in the book claims something different: "the value was moved out of composers[0].name". Now, I always thought a move is precisely not! the creation of a new object, but merely a new owner of the original value (at the same memory location!). However this is not the case. So my question: Do I have an error in the address-displays or is the notion of a "move" defined differently?

cafce25
  • 15,907
  • 4
  • 25
  • 31
P.Jo
  • 532
  • 3
  • 9
  • If you move between houses, does it mean you cease living in one house and start living in another, or does it mean that you keep living in the same house, but the owner of that house changed? – user3840170 Aug 25 '23 at 12:31

1 Answers1

1

Rust makes no guarantees that moving objects does not shift their position in memory. Sometimes it might, other times it might not.

In this case first_name is an object on the function's stack frame and is distinct (with a distinct address) from composers[0].name.

std::mem::replace(&mut composers[0].name, None) takes what was previously in composers[0].name and returns it by putting in on the stack in first_name. Therefore it is completely natural it has a different address from composers[0].name.

"Moving" in Rust parlance is an abstract concept and refers to moving the "ownership" of the object. It does not necessarily have a physical 1-to-1 translation to memory use or addresses.

Jared Smith
  • 19,721
  • 5
  • 45
  • 83
Emoun
  • 2,297
  • 1
  • 13
  • 20
  • Thanks - but first let me ask you to clarify my misunderstanding: If v is a vector, does &v give me the address of v (on the stack) or does it provide me the address of the heap location to the data where v is pointing to? – P.Jo Aug 25 '23 at 12:15
  • @P.Jo It will return address on the stack. To get address of the heap-allocated buffor use `Vec::as_ptr()` or do `&v[..]`/`v.as_slice()`. – Aleksander Krauze Aug 25 '23 at 12:19
  • Thank you! But with respect to my example, it still wouldn't change the fact that the heap address to which first_name is pointing is different from the original location of Some(....), right? – P.Jo Aug 25 '23 at 12:22
  • @AleksanderKrauze: One more question: Wouldn't I get the same address if I displayed &v[0]? – P.Jo Aug 25 '23 at 12:23
  • 1
    @P.Jo I agree with @AleksanderKrauze regarding you first question. For the second: `first_name` is not on the heap. The bytes containing the characters are, but the object controlling those characters is on the stack (its essentially a pointer and a size). If you get the address of the characters (with `composers[0].name.as_ref().unwrap().as_str()` and `first_name.as_ref().unwrap().as_str()`) you will see they are the same after the move – Emoun Aug 25 '23 at 13:59
  • @Emoun - thanks for the explanation. But regarding your last statement: If they have the same address, then what is the address of the None object which was passed into composers[0].name? first_name points to the Some(...) object and composers[0].name to a None object - or am I wrong? – P.Jo Aug 25 '23 at 14:15
  • @P.Jo Essentially `first_name` and `composer[0].name` contain 3 fields: 1) a flag signifying if it is `Some` or `None`, 2) a pointer to the characters on the heap, 3) an int signifying how many characters are in the heap. If the first field is `None`, the two others are ignored. (This is not technically true, but conceptually, that's what is going on). These 3 field are all together, on the stack and might change address. The string characters are on the heap (if `Some`) and don't (usually) change address. – Emoun Aug 25 '23 at 14:30