Skip to content

Instantly share code, notes, and snippets.

@jml
Created December 10, 2014 10:19
Show Gist options
  • Select an option

  • Save jml/a15415b26b9209d5e570 to your computer and use it in GitHub Desktop.

Select an option

Save jml/a15415b26b9209d5e570 to your computer and use it in GitHub Desktop.
rust cloning & ownership

(edited conversation from #rust, featuring jml, reem, and Yurume).

I have a value type Foo, implemented as a struct with methods that return Foo such that the struct is never mutated but only new ones returned. If I want one of those to return the value unchanged, do I have to clone? e.g. http://is.gd/rm4gmY

#[deriving(Eq, PartialEq, PartialOrd, Ord, Show, Clone)]
struct Foo {
  x: int,
}

impl Foo {
  fn new(x: int) -> Foo {
    Foo { x: x }
  }

  /// Returns a new version of self if the flag is set, otherwise returns
  /// the old one.
  fn some_operation(&self, p: bool) -> Foo {
    if p {
      Foo { x: 2 * self.x }
    } else {
      // XXX: Do I have to clone here? Is there some way to just return self?
      self.clone()
    }
  }
}

fn main() {
  let foo = Foo::new(5);
  println!("foo: {}", foo);
  let bar = foo.some_operation(true);
  let baz = foo.some_operation(false);
  println!("bar: {}", bar);
  println!("baz: {}", baz);
}

Because some_operation is taking a reference to &self, it needs to clone. (XXX - understand and explain why. I'm pretty sure I can figure this out for myself - jml)

The method can be changed to take self as a value, but then you won't be able to re-use one instance of Foo.

In this case you'll actually have no problem, since Foo is all Copy data, but if Foo is owned (meaning it contains data that has a destructor, has a destructor, or is not Copy) then calling a self method transfers ownership into the method, meaning you can no longer use it.

This example demonstrates the re-use problem:

#[deriving(Eq, PartialEq, PartialOrd, Ord, Show, Clone)]
struct Foo {
  xs: Vec<int>,
}

impl Foo {
  fn new(x: int) -> Foo {
    Foo { xs: vec![x] }
  }

  /// Returns a new version of self if the flag is set, otherwise returns
  /// the old one.
  fn some_operation(self, y: Option<int>) -> Foo {
    match y {
      Some(x) => {
        let mut ys = self.xs.clone();
        ys.push(x);
        Foo { xs: ys }
      },
      None => self,
    }
  }
}

fn main() {
  let foo = Foo::new(5);
  println!("foo: {}", foo);
  let bar = foo.some_operation(Some(4));
  let baz = foo.some_operation(None);
  println!("bar: {}", bar);
  println!("baz: {}", baz);
}

The first use moves it so the second use can't use it.

Changing Foo to store an Rc<Vec<T>> might make it possible to avoid the clone(). Rc only gives immutable access though. Really, this use case requires a persistent data structure.

Rust doesn't have one, but there are third-party libraries available, such as https://github.com/michaelwoerister/rs-persistent-datastructures.

For this particular use-case, it might be reasonable to roll my own linked list.

See also the Rust guide and the ownership guide.

If you do implement some persistent data structures, make an issue or PR in https://github.com/gankro/collect-rs.

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