# Redundant copy when move a variable

**URL:** <https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894>\
**Category:** language design\
**Created:** [May 22, 2024, 6:19pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894 "2024-05-22T18:19:10Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![zhangzhuang15](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/zhangzhuang15/32/12198_2.png) [@zhangzhuang15](https://internals.rust-lang.org/u/zhangzhuang15)\
**Post date:** [May 22, 2024, 6:19pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/1 "2024-05-22T18:19:10Z")

</div>

look at the code:

```rust
fn main() {
  struct Man {
      value: i16,
  }

  let m = Man { value: 10 };

  // m moves to n, but rust takes up a new memory
  // to store Man, not reuse the old memory,
  // i.e. the address of m.value is not the same as address of n.value,
  // it's really redundant!
  let n = m;
}

```

so, why design the language in this way ?

---

<div class="post-metadata">

**Author:** ![sahnehaeubchen](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sahnehaeubchen/32/9709_2.png) [@sahnehaeubchen](https://internals.rust-lang.org/u/sahnehaeubchen)\
**Post date:** [May 22, 2024, 7:10pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/2 "2024-05-22T19:10:07Z")

</div>

There is no extra memory used even without optimizations: [Compiler Explorer](https://rust.godbolt.org/z/W7ahrzz7P)

As soon as you want to observe the address, the compiler is hindered in the optimzations it can do. Printing addresses in code is therefore a bad way to inspect what optimizations happen to a program.

---

<div class="post-metadata">

**Author:** ![dlight](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dlight/32/8462_2.png) [@dlight](https://internals.rust-lang.org/u/dlight)\
**Post date:** [May 23, 2024, 12:46am UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/3 "2024-05-23T00:46:18Z")

</div>

> [@sahnehaeubchen](#):
>
> As soon as you want to observe the address, the compiler is hindered in the optimzations it can do.

Is there a way to view the address of something without inhibiting this specific optimization? This seems like a pretty large footgun.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [May 23, 2024, 1:18am UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/4 "2024-05-23T01:18:48Z")

</div>

You might be interested in reading through the discussion at and linked from:

> <https://github.com/rust-lang/unsafe-code-guidelines/issues/416>
>
> This question is tied up in several discussions:
> \- https://github.com/rust-lang…/rust/issues/71117
> \- https://github.com/rust-lang/rust/issues/68364
> \- https://github.com/rust-lang/unsafe-code-guidelines/issues/188
> \- https://github.com/rust-lang/rust/issues/112564
> 
> However it's all somewhat messy and mixed with other questions, making it all a bit hard to follow. Part of the question is where \`move\` operands are even allowed and which places they may work on.
> 
> https://github.com/rust-lang/rust/issues/112564 has some nice examples demonstrating that \`move\` is already meaningful today for function calls, and Miri fails to properly model that. I am not aware of similar examples for other uses of \`move\` (in regular MIR assignments). We should at least have a spec explaining today's codegen (and implement it in Miri); I don't know if there are plans for the future here that would require a tighter spec than that.
> 
> The function call example lends itself to a semantics \[like this\](https://github.com/rust-lang/rust/issues/71117#issuecomment-1590570738). At a high level, \`move\` would mean "load the value from the given place, and then deallocate the place it came from, before allocating any of the new memory needed for this statement -- that way the old memory may actually be reused for the new allocation".
> 
> However this only makes sense for certain place expressions (\`move(local.field)\` would have to be disallowed). And how does it look like for MIR assignments?
> 
> 
> \`\`\`
> local2 = Aggregate(... move(local) ...)
> \`\`\`
> could mean "load the MiniRust \`Value\` from \`local\`, then dealloc \`local\` like StorageDead, then StorageLive \`local2\`, then store the value in there". But when would that be useful? If \`local2\` was already live before, there's no advantage over just doing
> \`\`\`
> local2 = Aggregate(... copy(local) ...)
> StorageDead(local)
> \`\`\`
> so supposedly this should only be used for initializing a value?
> 
> Using custom MIR, can we have concrete examples of \`move\` in an assignment causing behavior that Miri cannot currently explain? (Specifically, having memory reused instead of doing a \`memcpy\`.) If no, could we in principle entirely get rid of \`move\` everywhere except for function calls? Cc @bjorn3

In short, the obvious and simple semantics have the allocation for bindings occur when they're declared and deallocation happen at the end of scope. This results in the (stack) allocations existing at the same time, thus being required to have disjoint addresses. If you don't inspect the address, the compiler can get away with colocating them, but if you do, it can't.

It is possible that moving from a local place would deallocate it. But even if this is the case, telling LLVM to optimize based on that is not straightforward, as LLVM grew up biased towards using C++ semantics.

---

<div class="post-metadata">

**Author:** ![the8472](https://avatars.discourse-cdn.com/v4/letter/t/0ea827/32.png) [@the8472](https://internals.rust-lang.org/u/the8472)\
**Post date:** [May 23, 2024, 4:29am UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/5 "2024-05-23T04:29:13Z")

</div>

> [@dlight](#):
>
> This seems like a pretty large footgun.

minor papercut¹ != large footgun²

¹most likely to affect explorative programming and not production use; tools to do this properly already exist  
²having one's feet shot is a _serious injury_ and potentially fatal, especially when using a large gun

---

<div class="post-metadata">

**Author:** ![zhangzhuang15](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/zhangzhuang15/32/12198_2.png) [@zhangzhuang15](https://internals.rust-lang.org/u/zhangzhuang15)\
**Post date:** [May 23, 2024, 4:26pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/6 "2024-05-23T16:26:19Z")

</div>

Thank you, I really like that discussion.

My code is simple, what I worry about is that if struct `Man` takes up too much memory, there'll be a memory copy(`let n = m`), it might slow down the performance of program.

In fact, I think rust doesn't need to do the copy job, because it will stop users using `m` after `let n = m` in compile time, unless users use unsafe rust code to get around, but in this case, it is duty of users to keep code safety, not rust.

Although it's not possible to take up too much memory on stack, I am still worried about memory copy. 😂

---

<div class="post-metadata">

**Author:** ![farnz](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/farnz/32/6587_2.png) [@farnz](https://internals.rust-lang.org/u/farnz)\
**Post date:** [May 23, 2024, 4:52pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/7 "2024-05-23T16:52:20Z")

</div>

In general, the compiler will elide the copy when it can. That said, eliding the copies is an optimization, and will not always fire. You should not worry about the memory copy unless you have an example case where the compiler should elide the memory copy, but did not (at which point, it can be worth posting here and asking people to help you investigate why the optimization did not fire).

It's also not worth worrying without a profiler showing you that the copies are a big deal in terms of final performance; CPUs are fast at copying data, and thus if it's rare, it's not a problem.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [May 23, 2024, 5:42pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/8 "2024-05-23T17:42:39Z")

</div>

There used to be a site tracking the stack efficiency of Rust by proxy statistic, but it seems to be down now, unfortunately. Generally, Rust/LLVM is decent at eliminating redundant copies of stack locals. It can struggle with large buffers of nontrivial contents sometimes, but it isn't something you typically need to worry about. Write code that does the right thing w.r.t. ownership and let the compiler do its job.

---

<div class="post-metadata">

**Author:** ![DragonDev1906](https://avatars.discourse-cdn.com/v4/letter/d/e68b1a/32.png) [@DragonDev1906](https://internals.rust-lang.org/u/DragonDev1906)\
**Post date:** [May 23, 2024, 5:45pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/9 "2024-05-23T17:45:01Z")

</div>

> unless you have an example case where the compiler should elide the memory copy, but did not

I've played around on [Compiler Explorer](https://rust.godbolt.org/z/38PvaWMEP) a bit because I was curious what exactly the output assembly does, and I was a bit surprised that the following (with `-C opt-level=3`) contains 2 calls to memcpy \[1\] (yes, they are cheap, but they are definitely avoidable here).

This does "inspect the address" becauses it gives a reference to `do_something`, so I understand why the compiler does not combine them, but since something like this is somewhat common pattern (move through calling functions, functions that return Self to be chained, declaration in a block that returns (and thus moves) something) I was expecting this to not copy the data around.

I cannot think of a reason (besides "that's how LLVM does things due to C") why those references wouldn't be allowed to have the same address, especially since due to the move we're guaranteed to not have a reference to the original `m`.

```rust
pub struct Wrapper(pub Man);

pub fn main(m: Man) {
    // Pass in the address of what we got from the caller
    do_something(&m);

    // Creates a copy, even with optimizations enabled
    let m = m;
    do_something(&m);
    // Does not call memcpy here, as there was no move
    do_something(&m);

    // Calls memcpy again, as it's technically a move
    let x = Wrapper(m);
    do_something(&x);
}

```

[Here (example 2)](https://rust.godbolt.org/z/8Poh4xYM5) is a more minimal example that still contains a memcpy with `-C opt-level=3`

[Here (example 3)](https://rust.godbolt.org/z/EjWbvbjrb) is a slightly more realistic one. `inner_fn` could for example be a builder pattern that modifies `m` (could be self).

I wouldn't have expected any of these examples to contain a call to memcpy. Maybe without optimizations but definitely not with optimizations enabled.

Am I missing something here or is that a fundamental limitation from the way things are passed to LLVM?

* * *

1. I'm using a `[i16; 1024]` to make the copy easier to find in the assembly.

---

<div class="post-metadata">

**Author:** ![farnz](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/farnz/32/6587_2.png) [@farnz](https://internals.rust-lang.org/u/farnz)\
**Post date:** [May 23, 2024, 6:11pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/10 "2024-05-23T18:11:18Z")

</div>

It looks like an unimplemented optimization to me. When I look at the LLVM IR, I don't see reason for [⚙ D153453 [MemCpyOpt] implement single BB stack-move optimization which unify the static unescaped allocas](https://reviews.llvm.org/D153453) to fire, since that's looking for a memcpy between two stack slots in the same basic block, but in your case, there's an input to the basic block that's being copied from.

Improving `MemCpyOpt` in LLVM would likely elide that allocation, too, since it would be able to see that the parameter `m` is not used after the memcpy.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [May 23, 2024, 7:50pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/11 "2024-05-23T19:50:33Z")

</div>

> [@DragonDev1906](#):
>
> This does "inspect the address" becauses it gives a reference to `do_something`,

Yeah, unfortunately it's not illegal for Rust code to do silly things, and thus LLVM is correct that in general it can't optimize certain things.

For example, `do_something` _could_, as far as LLVM knows (if it can't see the body), stash the `*const i16` in an `AtomicPtr`, and display something different to the console if you call it with the same address as the last time, at which point it would be important that certain stack slots are _not_ actually re-used.

So, ironically, one way to make that copy not copy it is to do a copy on _every_ call, which doesn't actually emit more copies into the output: [https://rust.godbolt.org/z/rP3nx6TxK](https://rust.godbolt.org/z/rP3nx6TxK)

But there's a bunch of issues open for things like this, like [Using ManuallyDrop causes allocas and memcpys that LLVM cannot remove · Issue #79914 · rust-lang/rust · GitHub](https://github.com/rust-lang/rust/issues/79914)

It'd be nice if it was better, certainly. And the latest LLVMs have gotten substantially better at it already.

---

<div class="post-metadata">

**Author:** ![DragonDev1906](https://avatars.discourse-cdn.com/v4/letter/d/e68b1a/32.png) [@DragonDev1906](https://internals.rust-lang.org/u/DragonDev1906)\
**Post date:** [May 23, 2024, 8:28pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/12 "2024-05-23T20:28:58Z")

</div>

> [@scottmcm](#):
>
> and thus LLVM is correct that in general it can't optimize certain things.

True, Rust would have to use lifetime analysis to know that that it's a pure function in terms of that variable/argument (but not other arguments, if that's the right term) and does not keep such a reference, and then **tell LLVM** that it can assume such a pointer copy will not happen (not sure if the latter is possible). As you said, the example you gave is valid for LLVM but not allowed in Rust.

I think that's one area where Rust has a (small) edge over C/C++, which do not have a borrow checker and thus cannot know the function doesn't store a pointer to it somewhere.

---

<div class="post-metadata">

**Author:** ![tczajka](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tczajka/32/8923_2.png) [@tczajka](https://internals.rust-lang.org/u/tczajka)\
**Post date:** [May 23, 2024, 9:24pm UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/13 "2024-05-23T21:24:20Z")

</div>

> [@zhangzhuang15](#):
>
> I am still worried about memory copy.

If you want to make sure to avoid copying, why not pass a reference to this data instead of moving it?

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [May 24, 2024, 1:06am UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/14 "2024-05-24T01:06:07Z")

</div>

> [@DragonDev1906](#):
>
> Rust would have to use lifetime analysis to know that that it's a pure function in terms of that variable/argument (but not other arguments, if that's the right term) and does not keep such a reference,

It doesn't even have to store the pointer. It can just remember the address. Lifetime analysis is not sufficient; analysis of the actual function body is required to derive that the pointer does not escape and the address is not observed.

This information could then be remembered in the signature and used during optimization of callers even without inlining, but it requires that analysis of the body.

---

<div class="post-metadata">

**Author:** ![DragonDev1906](https://avatars.discourse-cdn.com/v4/letter/d/e68b1a/32.png) [@DragonDev1906](https://internals.rust-lang.org/u/DragonDev1906)\
**Post date:** [May 24, 2024, 9:46am UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/15 "2024-05-24T09:46:22Z")

</div>

> [@CAD97](#):
>
> to derive that the pointer does not escape and the address is not observed.

True, it would be an observable difference in behavior if (for example) the function compares the addresses of arguments between invocations (thus preventing general optimization). I'm not quite sure, does Rust guarantee anything about the addresses (for example stored as a `usize`) **after the end of the lifetime**? At that point there is no guarantee about anything you'd read from that memory address (could segfault), but the address itself? Consider the following:

```rust
fn main() {
    let a = T::new();
    let b = T::new();
    outer(a);
    outer_2(b);
}

#[inline(never)]
fn outer(c: T) {
    do_stuff(&c);
}
#[inline(never)]
fn outer_2(d: T) {
    // Random variable that isn't used and can be optimized away
    let x = u64;
    do_stuff(&d);
}

```

By the time outer\_2 is called, `a` has been dropped (could also have been moved somewhere else by `outer`, but let's ignore that for now). `do_stuff` could store the address somewhere and it would be valid, save Rust (as long as there is no memory access using that address). If the compiler removes `x` (To be fair: I'm not 100% sure if it actually does that) or for some reason changes the stack layout of `outer_2` so it is different to that of `outer` (for example when enabling optimizations and completely eliminating a redundant stack variable), or any other change that affects the layout on the stack it would change if `b` is or is not at the same address as `a`. Therefore any comparison between the address `c` and `d` are pointing to is already completely up to the compiler/optimizer/LLVM as to what the result will be (as `a` and `b` are moved into the function).

The same goes the other way: Is there anything (guarantees given by the compiler) that prevents the Rust compiler from moving `k` around between calls to `do_stuff` in the following example? It is not `Pin` and thus not guaranteed not to move. (Disclaimer: I don't know if the compiler is allowed to add moves when it thinks it is useful. Probably not, as this is effectively the same as this problem: An optimization or change with an observable difference in behavior).

```rust
fn main() {
    let k = T::new();
    // I'm using mutable references in this example, so it's clear that
    // this only applies when the lifetime "ends" when `do_stuff` exits
    // and `do_stuff` can only keep the address (without any guarantees
    // about the underlying memory).
    do_stuff(&mut k);

    // Do a bunch of other stuff, allocations, function calls, whatever

    // Call it again
    do_stuff(&mut k);
}

```

I'd argue that relying on the address of something being different or equal to a later reference, which was created after the end of the lifetime of the previous reference is fundamentally unstable and (likely) not useful except perhaps for some kind of heuristic. That future versions of the compiler could at any time change something in the stack layout or how functions are called which would break any promises about the value of an address (not the value of the thing at that address) that may exist, hence I conclude that there are no such guarantees (at least across compiler versions and most likely across optimizer levels/settings).

So yes, when just considering one single optimization step only using lifetime analysis (instead of function body analysis) could result in a difference in observable behavior, but there are a lot of other things (especially across compiler versions) with a similar impact (basically any observation of stack memory addresses depend on this). Hence my question of **what guarantees even exist about the address** (again, not the memory location) **after the end of the lifetime** you got that address from.

(again, it's quite possible that I'm missing something here or that this would be considered a breaking change due to it not being documented anywhere)

I wouldn't be surprised if nobody depends on an address (no memory access) after the end of a lifetime, due to this.

Long story short: I'm curious what you think about this and if there are any such guarantees (e.g. across compiler versions)

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [May 24, 2024, 11:04am UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/16 "2024-05-24T11:04:15Z")

</div>

> [@DragonDev1906](#):
>
> I'm not quite sure, does Rust guarantee anything about the addresses (for example stored as a `usize`) **after the end of the lifetime**?

This scenario was already mentioned.

> [@CAD97](#):
>
> It is possible that moving from a local place would deallocate it. But even if this is the case, telling LLVM to optimize based on that is not straightforward, as LLVM grew up biased towards using C++ semantics.

* * *

> [@DragonDev1906](#):
>
> The same goes the other way: Is there anything (guarantees given by the compiler) that prevents the Rust compiler from moving `k` around between calls to `do_stuff` in the following example?

The arguments about observing the address of the same live stack slot are basically the same as the arguments about observing different live stack slots.

> [@CAD97](#):
>
> In short, the obvious and simple semantics have the allocation for bindings occur when they're declared and deallocation happen at the end of scope.

> [@scottmcm](#):
>
> For example, `do_something` _could_, as far as LLVM knows (if it can't see the body), stash the `*const i16` in an `AtomicPtr`, and display something different to the console if you call it with the same address as the last time,

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [August 22, 2024, 11:04am UTC](https://internals.rust-lang.org/t/redundant-copy-when-move-a-variable/20894/17 "2024-08-22T11:04:20Z")

</div>

Closed on August 22, 2024, 11:04am UTC

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
