rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
95.38k stars 12.29k forks source link

Mutable reference prevents value from being moved to its current address #127206

Open amab8901 opened 3 weeks ago

amab8901 commented 3 weeks ago

I tried this code:

fn main() {}

pub struct Structer {
    pub stringy: Vec<String>,
}

impl Structer {
    pub fn extend_stringy(&mut self) {
        self.stringy = self
            .stringy
            .into_iter()
            .map(|stringy| stringy + "extension")
            .collect::<Vec<String>>();
    }
}

I expected this:

self
    .stringy
    .into_iter()
    .map(|stringy| stringy + "extension")
    .collect::<Vec<String>>();

to be assigned to self.stringy, because the value is moved into the same location from where it came.

Instead, I got this error message:

cannot move out of `self.stringy` which is behind a mutable reference
move occurs because `self.stringy` has type `std::vec::Vec<std::string::String>`, which does not implement the `Copy` traitrustc[Click for full compiler diagnostic](rust-analyzer-diagnostics-view:/diagnostic%20message%20%5B0%5D?0#file%3A%2F%2F%2Fhome%2Famir%2Fsrc%2Fnice%2Fsrc%2Fmain.rs)
main.rs(11, 14): `self.stringy` moved due to this method call
collect.rs(344, 18): `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves `self.stringy`
main.rs(9, 24): you can `clone` the value and consume it, but this might not be your desired behavior: `<std::vec::Vec<std::string::String> as Clone>::clone(&`, `)`
main.rs(10, 21): consider cloning the value if the performance cost is acceptable: `.clone()`

Meta

rustc --version --verbose:

rustc 1.79.0 (129f3b996 2024-06-10)
binary: rustc
commit-hash: 129f3b9964af4d4a709d1383930ade12dfe7c081
commit-date: 2024-06-10
host: x86_64-unknown-linux-gnu
release: 1.79.0
LLVM version: 18.1.7
Backtrace

``` error[E0507]: cannot move out of `self.stringy` which is behind a mutable reference --> src/main.rs:9:24 | 9 | self.stringy = self | ________________________^ 10 | | .stringy | |____________________^ move occurs because `self.stringy` has type `Vec`, which does not implement the `Copy` trait 11 | .into_iter() | ----------- `self.stringy` moved due to this method call | note: `into_iter` takes ownership of the receiver `self`, which moves `self.stringy` --> /home/amir/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:344:18 | 344 | fn into_iter(self) -> Self::IntoIter; | ^^^^ help: you can `clone` the value and consume it, but this might not be your desired behavior | 9 ~ self.stringy = as Clone>::clone(&self 10 ~ .stringy) | help: consider cloning the value if the performance cost is acceptable | 10 | .stringy.clone() | ++++++++ For more information about this error, try `rustc --explain E0507`. error: could not compile `nice` (bin "nice") due to 1 previous error ```

quaternic commented 3 weeks ago

This is not a bug. The error is that you cannot move out from an exclusive reference (&mut T). The reference points to a Structer that is not owned by this function. When the function returns or unwinds, there must be a valid Structer still there.

Your example could be written without needing to move the value; just use .iter_mut() to modify each string in-place. Alternatively, if you need the values for some reason, you could use std::mem::take(&mut self.stringy) to temporarily replace it with an empty Vec. Lastly, if you really need to temporarily move something out and the above solutions are insufficient, you could consider using a crate like https://docs.rs/replace_with/latest/replace_with/

For help with compiler errors in the future, you should ask in URLO/Discord

@rustbot label -C-bug

veera-sivarajan commented 3 weeks ago

@rustbot label -needs-triage +C-discussion