How does Rust provide move semantics?

user3335 picture user3335 · Apr 7, 2015 · Viewed 12.2k times · Source

The Rust language website claims move semantics as one of the features of the language. But I can't see how move semantics is implemented in Rust.

Rust boxes are the only place where move semantics are used.

let x = Box::new(5);
let y: Box<i32> = x; // x is 'moved'

The above Rust code can be written in C++ as

auto x = std::make_unique<int>();
auto y = std::move(x); // Note the explicit move

As far as I know (correct me if I'm wrong),

  • Rust doesn't have constructors at all, let alone move constructors.
  • No support for rvalue references.
  • No way to create functions overloads with rvalue parameters.

How does Rust provide move semantics?

Answer

oli_obk picture oli_obk · Apr 7, 2015

I think it's a very common issue when coming from C++. In C++ you are doing everything explicitly when it comes to copying and moving. The language was designed around copying and references. With C++11 the ability to "move" stuff was glued onto that system. Rust on the other hand took a fresh start.


Rust doesn't have constructors at all, let alone move constructors.

You do not need move constructors. Rust moves everything that "does not have a copy constructor", a.k.a. "does not implement the Copy trait".

struct A;

fn test() {
    let a = A;
    let b = a;
    let c = a; // error, a is moved
}

Rust's default constructor is (by convention) simply an associated function called new:

struct A(i32);
impl A {
    fn new() -> A {
        A(5)
    }
}

More complex constructors should have more expressive names. This is the named constructor idiom in C++


No support for rvalue references.

It has always been a requested feature, see RFC issue 998, but most likely you are asking for a different feature: moving stuff to functions:

struct A;

fn move_to(a: A) {
    // a is moved into here, you own it now.
}

fn test() {
    let a = A;
    move_to(a);
    let c = a; // error, a is moved
}

No way to create functions overloads with rvalue parameters.

You can do that with traits.

trait Ref {
    fn test(&self);
}

trait Move {
    fn test(self);
}

struct A;
impl Ref for A {
    fn test(&self) {
        println!("by ref");
    }
}
impl Move for A {
    fn test(self) {
        println!("by value");
    }
}
fn main() {
    let a = A;
    (&a).test(); // prints "by ref"
    a.test(); // prints "by value"
}