Skip to content

move Copy values #3670

Open
Open

Description

@crlf0710

Copy values have special copy semantics, when they're moved away, the old place still have valid value alive. A lot of times this is not people want. For example, it's very easy to typo and wrongly use some temporary values after they have been sent away with assignment.

If possible, i'd propose a move expression:

`move` `(` expr `)`

The parenthesis are for avoiding the ambiguity with move closures.

Example:

let a = 42;
x.a = move(a);
double(&mut x.a);
dbg!(a);  // compile error

Alternatives:

  • unlet a;
  • a.move
  • let a = 42 in { ... };

Activity

  1. Diggsey commented on Jul 9, 2024

    @Diggsey
    Contributor

    For example, it's very easy to typo and wrongly use some temporary values after they have been sent away with assignment.

    I've never seen this be a problem, since by definition the old value is still valid? Do you have an example of where this can cause a problem?

    Assuming there's an example where this is a problem, why would someone remember to use the "move" modifier, if they forgot not to use the moved value? Typically these problems are solved in the type definition rather than the use-site, since it avoids this problem, but this solution already exists - you can just not implement Copy for types which you want to be moved.

  2. kennytm commented on Jul 9, 2024

    @kennytm
    Member
    macro_rules! unlet {
        ($a:ident) => {
            #[allow(unused_variables)]
            let $a: (/* this variable has been disabled in this scope */);
        }
    }
    let a = 42;
    x.a = a;
    unlet!(a); // <-- new
    double(&mut x.a);
    dbg!(a);  // error[E0381]: used binding `a` isn't initialized

    or you could just use { … } to impose the normal scoping rule...

    {
        let a = 42;
        x.a = a;
    } // <-- restrict scope of `a`
    double(&mut x.a);
    dbg!(a); // error[E0425]: cannot find value `a` in this scope
  3. crlf0710 commented on Jul 9, 2024

    @crlf0710
    MemberAuthor

    Yes, this unlet macro is good enough.

    I'm still a little curious why @Diggsey has never seen this be a problem. When writing algorithms, and there's more than one pointers or indices or something lightweight but copyable. I feel this is so common. Sorry I don't have code to share at the moment though. These types cannot be defined as non-copyable but this invalidation only happens within specific portion of algorithm.

  4. kennytm commented on Jul 10, 2024

    @kennytm
    Member

    These types cannot be defined as non-copyable but this invalidation only happens within specific portion of algorithm.

    Sorry but this speaks more about the API design of the code than the Rust language. It is trivial to wrap the Copy type in a non-Copy structure such as

    #[repr(transparent)]
    #[derive(Debug)]
    struct NoCopy<T>(T);
    impl<T> NoCopy<T> {
        pub fn new(inner: T) -> Self {
            Self(inner)
        }   
        pub fn into_inner(self) -> T {
            self.0
        }
    }
        let a = NoCopy::new(42); // <-- wrap the type into NoCopy
        x.a = a.into_inner(); // <-- consume the NoCopy
        double(&mut x.a);
        dbg!(a);  // error[E0382]: use of moved value: `a`

    You could further enhance the safety by making the API accept specialized NoCopy-styled wrapper types only, and also making their new() and into_inner() private to the low-level modules.

    And even if this RFC is implemented, as @Diggsey said in the 2nd paragraph of their comment, if you forget to annotate a move(a) the compiler will never be able to notice because copying is still valid, so this RFC offered no safety guard against misuse. Meanwhile if the variable a itself is declared NoCopy<i32> instead (i.e. "solved in the type definition rather than the use-site"), Rust can easily statically prevent double-use.

  5. clarfonthey commented on Aug 3, 2024

    @clarfonthey
    Contributor

    Note that there's also this RFC which attempts to solve a similar no-move problem: #3518

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions