goo
goo

Reputation: 2280

cannot use `*self` because it was mutably borrowed

I'm running into this issue. My code looks like this:

struct MyStruct {
  name:String
}

impl MyStruct {
  fn foo(&mut self) {
    self.name = "Bob";
    self.bar(self, "Foo");
  }

  fn bar(&mut self, my_struct: MyStruct, last_name: String) {
    self.name = format!("{} {}", my_struct.name, last_name)
  }
}

However, I'm getting this error error: mismatched types: expected 'MyStruct', found '&mut MyStruct' (expected struct MyStruct, found &-ptr) in my foo method. I understand that Rust has not yet reached its 1.0 release but I'd like to understand pointers in Rust.

What am I doing wrong and what are some other ways to fix it?

What I've tried:

let f = *self;
self.bar(f, "Foo"); // works using the "raw pointer"

self.bar(*self, "Foo"); // doesn't work.. Error message: "borrow of `*self` occurs here"

rust version: rustc 0.12.0-pre-nightly (09abbbdaf 2014-09-11 00:05:41 +0000)

Upvotes: 2

Views: 2723

Answers (2)

Vladimir Matveev
Vladimir Matveev

Reputation: 127771

First of all, literals like "Foo" are not Strings, they are &'static strs. The difference is, String is an actual allocated buffer containing string contents and &str is just a view into existing buffer. &'static strs are, therefore, views into some statically allocated data. You can't pass &str as a String, so your example should look something like this:

struct MyStruct {
  name:String
}

impl MyStruct {
  fn foo(&mut self) {
    self.name = "Bob".to_string();
    self.bar(self, "Foo");
  }

  fn bar(&mut self, my_struct: MyStruct, last_name: &str) {
    self.name = format!("{} {}", my_struct.name, last_name)
  }
}

However, this doesn't work, as you have already noticed, and won't even after the String -> &str change:

self.bar(self, "Foo")

Neither will this:

self.bar(*self, "Foo")

nor this:

let f = *self;  // this is not a "raw pointer", BTW, this is just a dereference
self.bar(f, "Foo")  

The first variant won't work simply because the types do not match: self is &mut MyStruct, and bar() expects plain MyStruct as the first argument.

The second and the third variants are basically equivalent and could in fact work if MyStruct was a Copy type (if it implemented Copy trait). It isn't, however, because it contains String, which is not a Copy type since it has an associated destructor. Because of this the compiler does not implement Copy for MyStruct, and values of such types can only be moved around, not copied.

But you can't move a value out of &mut reference because a reference is a non-owning view of data, so *self just won't compile.

Moreover, there is a further obstacle. Because you want to pass self into bar() somehow, you can't do it using references, which would be the best choice otherwise. That is, this won't work:

fn bar(&mut self, my_struct: &MyStruct, last_name: &str) {
    self.name = format!("{} {}", my_struct.name, last_name);
}

self.bar(self, "Foo");  // self as an argument is auto-reborrowed from &mut to &

This happens because Rust does not allow having &mut reference into some data at the same time with any other reference to the same data, and this is exactly what would happen here: self.bar(self, "Foo") method call requires &mut MyStruct as its target but also &MyStruct as its argument, and because self is passed as both of them, the compiler would reject it.

Hence if you want to keep the code as close as possible to your original intent, your best bet would be using Clone. Derive Clone for your structure:

#[deriving(Clone)]
struct MyStruct {
    name: String
}

Then you can use clone() method to obtain a copy of your structure:

impl MyStruct {
  fn foo(&mut self) {
    self.name = "Bob".to_string();
    let c = self.clone();
    self.bar(c, "Foo");
  }

  fn bar(&mut self, my_struct: MyStruct, last_name: &str) {
    self.name = format!("{} {}", my_struct.name, last_name)
  }
}

However, it is possible that your code needs restructuring. Often such things could be expressed more clearly, for example, by moving bar() call up to foo() caller.

I highly recommend reading the official Rust guide which explains such concepts as ownership, borrowing and references. After that the reason why your code does not work should be more clear.

Upvotes: 2

IdolfHatler
IdolfHatler

Reputation: 176

You have two problems in your code.

The first is a minor problem with your strings. When you type "Bob", it has type &'static str, while you want String in at least some of the places in your code. This is easily fixed by replacing "Bob" with "Bob".to_string().

The second problem is that, you are essentially trying to give the same value to the function twice. You can do that in rust, but you need to be very careful about how you do it.

The easiest way to fix, would be to clone the object, thus giving two different (but equal) objects. Your example in this case would be:

#[deriving(Clone)]
struct MyStruct {
  name:String
}

impl MyStruct {
  fn foo(&mut self) {
    self.name = "Bob".to_string();
    let cloned = self.clone();
    self.bar(clone, "Foo".to_string());
  }

  fn bar(&mut self, my_struct: MyStruct, last_name: String) {
    self.name = format!("{} {}", my_struct.name, last_name)
  }
}

Upvotes: 1

Related Questions