Reputation: 2280
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
Reputation: 127771
First of all, literals like "Foo"
are not String
s, they are &'static str
s. The difference is, String
is an actual allocated buffer containing string contents and &str
is just a view into existing buffer. &'static str
s 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
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