不能使用`* self`,因为它是可变的借来的

时间:2014-09-14 16:07:35

标签: rust

我遇到了这个问题。我的代码如下所示:

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)
  }
}

但是,我在error: mismatched types: expected 'MyStruct', found '&mut MyStruct' (expected struct MyStruct, found &-ptr)方法中收到此错误foo。我知道Rust还没有达到它的1.0版本,但是我想了解Rust中的指针。

我做错了什么以及其他一些解决方法?

我尝试过的事情:

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"

生锈版:rustc 0.12.0-pre-nightly (09abbbdaf 2014-09-11 00:05:41 +0000)

2 个答案:

答案 0 :(得分:1)

您的代码中存在两个问题。

第一个是你的字符串的一个小问题。当您键入"Bob"时,它的类型为&'static str,而您希望代码中至少某些位置显示String。将"Bob"替换为"Bob".to_string()即可轻松解决此问题。

第二个问题是,你实际上是在尝试两次为函数赋予相同的值。你可以在生锈中做到这一点,但你需要非常小心你是如何做到的。

最简单的修复方法是克隆对象,从而提供两个不同(但相等)的对象。在这种情况下,您的示例是:

#[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)
  }
}

答案 1 :(得分:1)

首先,像"Foo"这样的文字不是String,而是&'static str。区别在于,String是包含字符串内容的实际分配缓冲区,&str只是现有缓冲区的视图。因此,&'static str是对某些静态分配数据的视图。您无法将&str作为String传递,因此您的示例应如下所示:

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)
  }
}

然而,正如您已经注意到的那样,这并不起作用,即使在String -> &str更改后也无法胜出:

self.bar(self, "Foo")

这也不会:

self.bar(*self, "Foo")

也不是:

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

第一个版本仅仅因为类型不匹配而无法工作:self&mut MyStructbar()期望普通MyStruct作为第一个参数。

如果MyStructCopy类型(如果它实现了Copy特征),那么第二个和第三个变体基本上是等效的,可以实际上有用。但是,它并不是因为它包含String,因为它有一个关联的析构函数,所以它不是Copy类型。因此,编译器不会为Copy实现MyStruct,并且这些类型的值只能移动而不能复制。

但是您无法从&mut引用中移出某个值,因为引用是非拥有数据视图,因此*self刚赢了&#39 ; t编译。

此外,还有一个障碍。因为你想以某种方式将self传递给bar(),所以你不能使用引用,否则这将是最好的选择。也就是说,这不会起作用:

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 &

这是因为Rust不允许&mut同时引用某些数据以及对同一数据的任何其他引用,这正是这里会发生的事情:self.bar(self, "Foo")方法调用需要&mut MyStruct作为其目标,但&MyStruct作为其参数,并且因为self作为它们两者传递,编译器将拒绝它。

因此,如果您希望代码尽可能接近原始意图,那么最好的选择是使用Clone。为您的结构导出Clone

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

然后您可以使用clone()方法获取结构的副本:

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)
  }
}

但是,您的代码可能需要重组。通常可以更清楚地表达此类内容,例如,将bar()调用移至foo()来电。

我强烈建议您阅读the official Rust guide,其中解释了所有权,借阅和参考等概念。之后,您的代码无效的原因应该更加明确。