Why does “move” in Rust not actually move?
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?
rust
add a comment |
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?
rust
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
Nov 25 '18 at 9:04
@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) . Butmove
in c++ is used when we use&&
andstd::move
to trigger move constructor. After beingmoved
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
Nov 25 '18 at 9:36
1
And how would you move an array in a C++ move constructor ? Use box for big thing.
– Stargateur
Nov 25 '18 at 19:39
add a comment |
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?
rust
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?
rust
rust
edited Nov 25 '18 at 12:51
Boann
37.2k1290121
37.2k1290121
asked Nov 25 '18 at 8:31
YangKeaoYangKeao
623
623
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
Nov 25 '18 at 9:04
@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) . Butmove
in c++ is used when we use&&
andstd::move
to trigger move constructor. After beingmoved
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
Nov 25 '18 at 9:36
1
And how would you move an array in a C++ move constructor ? Use box for big thing.
– Stargateur
Nov 25 '18 at 19:39
add a comment |
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
Nov 25 '18 at 9:04
@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) . Butmove
in c++ is used when we use&&
andstd::move
to trigger move constructor. After beingmoved
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
Nov 25 '18 at 9:36
1
And how would you move an array in a C++ move constructor ? Use box for big thing.
– Stargateur
Nov 25 '18 at 19:39
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
Nov 25 '18 at 9:04
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
Nov 25 '18 at 9:04
@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
Nov 25 '18 at 9:36
@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
Nov 25 '18 at 9:36
1
1
And how would you move an array in a C++ move constructor ? Use box for big thing.
– Stargateur
Nov 25 '18 at 19:39
And how would you move an array in a C++ move constructor ? Use box for big thing.
– Stargateur
Nov 25 '18 at 19:39
add a comment |
1 Answer
1
active
oldest
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>
.
2
Or pass the function an&mut f
and return nothing, which would be idiomatic in this case.
– starblue
Nov 25 '18 at 8:57
1
In this particular case, the move could actually be avoided. The variablef
is created in the stack frame ofmain()
, and the compiler could statically determine that it's not necessary to move it into the stack frame offoo()
, since it will be copied back to its original location anyway. But even in a release build withfoo()
marked as#[inline(always)]
, the compiler still unnecessarily copies the array twice.
– Sven Marnach
Nov 25 '18 at 9:09
I understand how it works. But the ownership provide more information for compiler, with these information we can use same part of memory without any secure problem . I think it's a big optimization in some case. But rust haven't done this ( but actually when the function is inlined, it will use the same memory without copy ).
– YangKeao
Nov 25 '18 at 9:24
1
@YangKeao Even when inlining the function, the copy does not seem to get elided, but maybe that's because of taking the addresses, and wouldn't happen if we remove theprintln
invocations? My understanding so far has been that Rust should be able to optimise that case.
– Sven Marnach
Nov 25 '18 at 12:22
1
@YangKeao I guess these kinds of optimisations aren't meant as guarantees. If you want to guarantee that no copy is happening, pass around references of boxes instead.
– Sven Marnach
Nov 25 '18 at 12:59
|
show 2 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53465843%2fwhy-does-move-in-rust-not-actually-move%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
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>
.
2
Or pass the function an&mut f
and return nothing, which would be idiomatic in this case.
– starblue
Nov 25 '18 at 8:57
1
In this particular case, the move could actually be avoided. The variablef
is created in the stack frame ofmain()
, and the compiler could statically determine that it's not necessary to move it into the stack frame offoo()
, since it will be copied back to its original location anyway. But even in a release build withfoo()
marked as#[inline(always)]
, the compiler still unnecessarily copies the array twice.
– Sven Marnach
Nov 25 '18 at 9:09
I understand how it works. But the ownership provide more information for compiler, with these information we can use same part of memory without any secure problem . I think it's a big optimization in some case. But rust haven't done this ( but actually when the function is inlined, it will use the same memory without copy ).
– YangKeao
Nov 25 '18 at 9:24
1
@YangKeao Even when inlining the function, the copy does not seem to get elided, but maybe that's because of taking the addresses, and wouldn't happen if we remove theprintln
invocations? My understanding so far has been that Rust should be able to optimise that case.
– Sven Marnach
Nov 25 '18 at 12:22
1
@YangKeao I guess these kinds of optimisations aren't meant as guarantees. If you want to guarantee that no copy is happening, pass around references of boxes instead.
– Sven Marnach
Nov 25 '18 at 12:59
|
show 2 more comments
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>
.
2
Or pass the function an&mut f
and return nothing, which would be idiomatic in this case.
– starblue
Nov 25 '18 at 8:57
1
In this particular case, the move could actually be avoided. The variablef
is created in the stack frame ofmain()
, and the compiler could statically determine that it's not necessary to move it into the stack frame offoo()
, since it will be copied back to its original location anyway. But even in a release build withfoo()
marked as#[inline(always)]
, the compiler still unnecessarily copies the array twice.
– Sven Marnach
Nov 25 '18 at 9:09
I understand how it works. But the ownership provide more information for compiler, with these information we can use same part of memory without any secure problem . I think it's a big optimization in some case. But rust haven't done this ( but actually when the function is inlined, it will use the same memory without copy ).
– YangKeao
Nov 25 '18 at 9:24
1
@YangKeao Even when inlining the function, the copy does not seem to get elided, but maybe that's because of taking the addresses, and wouldn't happen if we remove theprintln
invocations? My understanding so far has been that Rust should be able to optimise that case.
– Sven Marnach
Nov 25 '18 at 12:22
1
@YangKeao I guess these kinds of optimisations aren't meant as guarantees. If you want to guarantee that no copy is happening, pass around references of boxes instead.
– Sven Marnach
Nov 25 '18 at 12:59
|
show 2 more comments
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>
.
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>
.
edited Nov 25 '18 at 9:56
answered Nov 25 '18 at 8:48
Sebastian RedlSebastian Redl
50.3k476116
50.3k476116
2
Or pass the function an&mut f
and return nothing, which would be idiomatic in this case.
– starblue
Nov 25 '18 at 8:57
1
In this particular case, the move could actually be avoided. The variablef
is created in the stack frame ofmain()
, and the compiler could statically determine that it's not necessary to move it into the stack frame offoo()
, since it will be copied back to its original location anyway. But even in a release build withfoo()
marked as#[inline(always)]
, the compiler still unnecessarily copies the array twice.
– Sven Marnach
Nov 25 '18 at 9:09
I understand how it works. But the ownership provide more information for compiler, with these information we can use same part of memory without any secure problem . I think it's a big optimization in some case. But rust haven't done this ( but actually when the function is inlined, it will use the same memory without copy ).
– YangKeao
Nov 25 '18 at 9:24
1
@YangKeao Even when inlining the function, the copy does not seem to get elided, but maybe that's because of taking the addresses, and wouldn't happen if we remove theprintln
invocations? My understanding so far has been that Rust should be able to optimise that case.
– Sven Marnach
Nov 25 '18 at 12:22
1
@YangKeao I guess these kinds of optimisations aren't meant as guarantees. If you want to guarantee that no copy is happening, pass around references of boxes instead.
– Sven Marnach
Nov 25 '18 at 12:59
|
show 2 more comments
2
Or pass the function an&mut f
and return nothing, which would be idiomatic in this case.
– starblue
Nov 25 '18 at 8:57
1
In this particular case, the move could actually be avoided. The variablef
is created in the stack frame ofmain()
, and the compiler could statically determine that it's not necessary to move it into the stack frame offoo()
, since it will be copied back to its original location anyway. But even in a release build withfoo()
marked as#[inline(always)]
, the compiler still unnecessarily copies the array twice.
– Sven Marnach
Nov 25 '18 at 9:09
I understand how it works. But the ownership provide more information for compiler, with these information we can use same part of memory without any secure problem . I think it's a big optimization in some case. But rust haven't done this ( but actually when the function is inlined, it will use the same memory without copy ).
– YangKeao
Nov 25 '18 at 9:24
1
@YangKeao Even when inlining the function, the copy does not seem to get elided, but maybe that's because of taking the addresses, and wouldn't happen if we remove theprintln
invocations? My understanding so far has been that Rust should be able to optimise that case.
– Sven Marnach
Nov 25 '18 at 12:22
1
@YangKeao I guess these kinds of optimisations aren't meant as guarantees. If you want to guarantee that no copy is happening, pass around references of boxes instead.
– Sven Marnach
Nov 25 '18 at 12:59
2
2
Or pass the function an
&mut f
and return nothing, which would be idiomatic in this case.– starblue
Nov 25 '18 at 8:57
Or pass the function an
&mut f
and return nothing, which would be idiomatic in this case.– starblue
Nov 25 '18 at 8:57
1
1
In this particular case, the move could actually be avoided. The variable
f
is created in the stack frame of main()
, and the compiler could statically determine that it's not necessary to move it into the stack frame of foo()
, since it will be copied back to its original location anyway. But even in a release build with foo()
marked as #[inline(always)]
, the compiler still unnecessarily copies the array twice.– Sven Marnach
Nov 25 '18 at 9:09
In this particular case, the move could actually be avoided. The variable
f
is created in the stack frame of main()
, and the compiler could statically determine that it's not necessary to move it into the stack frame of foo()
, since it will be copied back to its original location anyway. But even in a release build with foo()
marked as #[inline(always)]
, the compiler still unnecessarily copies the array twice.– Sven Marnach
Nov 25 '18 at 9:09
I understand how it works. But the ownership provide more information for compiler, with these information we can use same part of memory without any secure problem . I think it's a big optimization in some case. But rust haven't done this ( but actually when the function is inlined, it will use the same memory without copy ).
– YangKeao
Nov 25 '18 at 9:24
I understand how it works. But the ownership provide more information for compiler, with these information we can use same part of memory without any secure problem . I think it's a big optimization in some case. But rust haven't done this ( but actually when the function is inlined, it will use the same memory without copy ).
– YangKeao
Nov 25 '18 at 9:24
1
1
@YangKeao Even when inlining the function, the copy does not seem to get elided, but maybe that's because of taking the addresses, and wouldn't happen if we remove the
println
invocations? My understanding so far has been that Rust should be able to optimise that case.– Sven Marnach
Nov 25 '18 at 12:22
@YangKeao Even when inlining the function, the copy does not seem to get elided, but maybe that's because of taking the addresses, and wouldn't happen if we remove the
println
invocations? My understanding so far has been that Rust should be able to optimise that case.– Sven Marnach
Nov 25 '18 at 12:22
1
1
@YangKeao I guess these kinds of optimisations aren't meant as guarantees. If you want to guarantee that no copy is happening, pass around references of boxes instead.
– Sven Marnach
Nov 25 '18 at 12:59
@YangKeao I guess these kinds of optimisations aren't meant as guarantees. If you want to guarantee that no copy is happening, pass around references of boxes instead.
– Sven Marnach
Nov 25 '18 at 12:59
|
show 2 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53465843%2fwhy-does-move-in-rust-not-actually-move%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
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
Nov 25 '18 at 9:04
@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) . Butmove
in c++ is used when we use&&
andstd::move
to trigger move constructor. After beingmoved
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
Nov 25 '18 at 9:36
1
And how would you move an array in a C++ move constructor ? Use box for big thing.
– Stargateur
Nov 25 '18 at 19:39