Skip to content
Select theme

How move semantics work in Rust

Short answer: a move shifts the value’s bytes to a new place and stops counting the old place as initialised. Both of those are compile-time decisions. Whatever the value points at lives elsewhere and stays where it is, so moving a String copies three words no matter how long the text is.

Note where a String’s text lives, then move the String:

same buffer: true
bytes copied: 24

as_ptr returns the address of the heap buffer, and both bindings report the same one after the move. The five bytes of text weren’t copied anywhere. What changed location is the String value itself — a pointer, a length and a capacity, the same three words from why Rust moves values. size_of_val puts a number on it: 24 bytes on a 64-bit machine.

That number is the type’s, not the text’s, so a long string is no dearer to move than a short one:

short: 24 bytes
long: 24 bytes
long is 100000 characters

Moving into a function doesn’t change any of this. A parameter is another binding, and the buffer address on the far side of the call is the one you started with.

A String keeps its text on the heap, which is why its move is three words however long the text is. Put the payload inside the value instead and all of it travels:

Blob is 1048 bytes
last data byte: 7
tag: payload

1024 of those bytes are the array and 24 are the String, and the move relocates all 1048: the array turns up under the new name at a different address, with its contents intact. That’s why size_of::<Blob>() is the price of moving one. ptr::read describes the copy in one line — it “creates a bitwise copy of T, regardless of whether T is Copy” — and a move is that copy with the source retired afterwards. Most types are a handful of words, so this rarely matters. When it does, the fix is to keep the payload behind a pointer, which is what String, Vec and Box all do.

The old binding isn’t zeroed and isn’t freed. The compiler stops treating it as initialised and refuses to read it, which is the state the previous article works through. The bytes at that spot didn’t change. What changed is what the compiler lets you do with them.