可变引用

现在,你的访问器方法应该看起来像这样:

#![allow(unused)]
fn main() {
impl Ticket {
    pub fn title(&self) -> &String {
        &self.title
    }

    pub fn description(&self) -> &String {
        &self.description
    }

    pub fn status(&self) -> &String {
        &self.status
    }
}
}

在这里那里撒上一点&就搞定了!我们现在有一种方式可以在不消耗Ticket实例的过程中访问其字段。接下来,让我们看看如何通过添加设置器方法来增强我们的Ticket结构体。

设置器

设置器方法允许用户更改Ticket的私有字段值,同时确保其不变性得到尊重(例如,你不能将Ticket的标题设置为空字符串)。

在Rust中有两种常见的设置器实现方式:

  • self作为输入。
  • &mut self作为输入。

self作为输入

第一种方法如下所示:

#![allow(unused)]
fn main() {
impl Ticket {
    pub fn set_title(mut self, new_title: String) -> Self {
        // 验证新标题 [...]
        self.title = new_title;
        self
    }
}
}

它获取self的所有权,更改标题,并返回修改后的Ticket实例。你可以这样使用它:

#![allow(unused)]
fn main() {
let ticket = Ticket::new("标题".into(), "描述".into(), "待办".into());
let ticket = ticket.set_title("新标题".into());
}

由于set_title获取self的所有权(即消耗它),我们需要将结果重新赋值给一个变量。在上面的例子中,我们利用变量覆盖来重用相同的变量名:当你使用与现有变量相同的名字声明新变量时,新变量会覆盖旧变量。这是Rust代码中的一个常见模式。

当需要一次性更改多个字段时,self-设置器工作得相当不错:你可以将多个调用串联在一起!

#![allow(unused)]
fn main() {
let ticket = ticket
    .set_title("新标题".into())
    .set_description("新描述".into())
    .set_status("进行中".into());
}

&mut self作为输入

第二种设置器方法,使用&mut self,则是这样的:

#![allow(unused)]
fn main() {
impl Ticket {
    pub fn set_title(&mut self, new_title: String) {
        // 验证新标题 [...]
        
        self.title = new_title;
    }
}
}

这次,该方法以可变引用的形式接收self作为输入,更改标题,就这样。没有返回任何东西。

你可以这样使用它:

#![allow(unused)]
fn main() {
let mut ticket = Ticket::new("标题".into(), "描述".into(), "待办".into());
ticket.set_title("新标题".into());

// 使用已修改的ticket
}

所有权保留在调用者手中,所以原始的ticket变量仍然是有效的。我们不需要重新分配结果。但是,我们需要将ticket标记为可变的,因为我们正在对其采取可变引用。

&mut-设置器有一个缺点:你不能链式调用多个设置。由于它们不返回修改后的Ticket实例,你不能在第一个调用的结果上再调用另一个设置器。你必须分别调用每个设置器:

#![allow(unused)]
fn main() {
ticket.set_title("新标题".into());
ticket.set_description("新描述".into());
ticket.set_status("进行中".into());
}

参考

  • 本节练习位于 exercises/03_ticket_v1/07_setters