13
votes

In the below example:

struct Foo {
    a: [u64; 100000],
}

fn foo(mut f: Foo) -> Foo {
    f.a[0] = 99999;
    f.a[1] = 99999;
    println!("{:?}", &mut f as *mut Foo);

    for i in 0..f.a[0] {
        f.a[i as usize] = 21444;
    }

    return f;
}
fn main(){
    let mut f = Foo {
        a:[0;100000]
    };

    println!("{:?}", &mut f as *mut Foo);
    f = foo(f);
    println!("{:?}", &mut f as *mut Foo);
}

I find that before and after passing into the function foo, the address of f is different. Why does Rust copy such a big struct everywhere but not actually move it (or achieve this optimization)?

I understand how stack memory works. But with the information provided by ownership in Rust, I think the copy can be avoided. The compiler unnecessarily copies the array twice. Can this be an optimization for the Rust compiler?

1
C++ does essentially the same. Huge data structures can be dumped on the stack without the & in the function or method declaration indicating that you want to pass a reference. (In my case that was a bug, 200K were dumped on a 16K stack in an embedded system. Since there was no memory protection several other stacks were also wiped out, and the system crashed shortly afterwards in unrelated code. Took me a few hours to find the single missing &.)starblue
@starblue With or without & can have lot of difference. Pass reference of a variable will share the same memory. But without & the copy constructor ( or simply copy ) will be used to create the argument variable ( it has no connection with the variable passed in after copy) . But move in c++ is used when we use && and std::move to trigger move constructor. After being moved into a function, the variable cannot be used. So the move in C++ has nearly the same semantic with rust (without security provided by ownership system), but the performance is different.YangKeao
And how would you move an array in a C++ move constructor ? Use box for big thing.Stargateur

1 Answers

10
votes

A move is a memcpy followed by treating the source as non-existent.

Your big array is on the stack. That's just the way Rust's memory model works: local variables are on the stack. Since the stack space of foo is going away when the function returns, there's nothing else the compiler can do except copy the memory to main's stack space.

In some cases, the compiler can rearrange things so that the move can be elided (source and destination are merged into one thing), but this is an optimization that cannot be relied on, especially for big things.

If you don't want to copy the huge array around, allocate it on the heap yourself, either via a Box<[u64]>, or simply by using Vec<u64>.