What does the 'move' keyword actually works?


#1

I have this piece of code:

use std::thread;

struct StackItem(i32);
fn main() {
    let mut x = StackItem(32);
    
    let _ = thread::spawn(move || x).join();
    x.0 = 12;
    
    //println!("{:?}", x.0)
}

My problem is that x is moved into another thread, but why i still have the ability to re-assign x.0 = 12 without any compiling error?

EDIT: palyground https://is.gd/UscxUb


#2

You don’t really have the ability to re-assign x.0 = 12, take a look at this playground example: http://play.integer32.com/?gist=681f16fdf930577065e591d96cb3f325&version=undefined

Note that println!("inner: {:?}", wrapper()); prints 32 instead of the reassigned 12.

So why does it look like you can? I mean, x.0 = 12 is in there in the code, does it even do anything? My guess it, that some kind of dead code elimination might kill it before it causes any errors. It still is weird and I would love to hear the opinion of someone who actually knows this.


#3

Because it is considered newly created (rustc sees it is equivalent to x = StackItem(12);). Moving and uncommenting println! before that statement will correctly trigger an error.

I think that rustc recognizes a tuple struct with one element equivalent to the element’s value when it analyzes the ownership; I don’t see the same treatment when StackItem is replaced by a record struct with one element.


#4

I would never expect this behavior.

Can we get a warning for this/make it an error?


#5

Personnally, I would report this as a rustc bug.


#6

It does already trigger an error when the println!("outer: {:?}", x.0); is uncommented where is is.

But for the assignment, it looks like rustc does indeed create a new variable. I did dig into the LLVM code to find the original assignment

// This seems to be the result of `let mut x = StackItem(32);`.
%0 = getelementptr inbounds %StackItem, %StackItem* %x, i32 0, i32 0, !dbg !246
store i32 32, i32* %0, !dbg !246

// This seems to be the result of `x.0 = 1337;`.
%6 = getelementptr inbounds %StackItem, %StackItem* %x, i32 0, i32 0, !dbg !248
store i32 1337, i32* %6, !dbg !248

I did change the number to 1337 to make it easier to find in the LLVM IR. Note that I have no idea how LLVM IR works at all.

I guess reporting a rustc bug as @HadrienG suggested is probably a good idea. Or maybe suggest it as a clippy lint or something?

Edit: I just tested the same thing in --release mode and the 1337 completely vanished from both LLVM IR and ASM outputs.


#7

Seems this has already been noted before: https://github.com/rust-lang/rust/issues/21232