没有行为的类型功能有限,并且通常情况下我们希望类型具有函数或方法,以便我们可以返回它们的实例而不是手动构造它们,或者使我们能够操作自定义类型中的字段。这可以通过 impl块来实现,它被视作某个类型提供实现。我们可以为所有自定义类型或包装器提供实现。
结构体上的 impl 块
我们可以使用两种机制向之前定义的结构体 Player 中添加行为:一种是类似构造函数的函数,它接收一个名称并为 Person 中的其余字段设置默认值,另一种是设置 Person 的 friends 字段的 getter 和 setter 方法。
struct Player {name: String,iq: u8,friends: u8}impl Player {fn with_name(name: &str) -> Player {Player {name: name.to_string(),iq: 100,friends: 100}}fn get_friends(&self) -> u8 {self.friends}fn set_friends(&mut self,count: u8){self.friends = count;}}fn main() {let mut player = Player::with_name("Dave");player.set_friends(23);println!("{}'s friends count: {}",player.name,player.get_friends());//另一种调用实例方法的方式let _ = Player::get_friends(&player);}
我们指定关键字`impl`,然后指定我们希望实现方法的类型,后跟一对花括号。在花括号中,我们可以编写两种方法:
- 关联方法:
- 该方法没有
self类型作为第1个参数。with_name方法被称为关联方法,因为它没有使用self作为第1个参数。它类似于面向对象编程语言中的静态方法。这些方法的类型自身上即可调用,并且不需要类型的实例来调用。通过在方法名称前加上结构体名称和双冒号来调用关联方法,如下所示: Player::with_name("Dave");
- 该方法没有
- 实例方法:
- 将
self作为第1个参数的函数。这里的self类似于Python 中的 self,并指向实现该方法的实例(这里是 Player)。因此,get_friends()方法只能在已创建的结构体上实例调用:
- 将
let mut player = Player::with_name("Dave");player.set_friends(23);
如果我们使用关联方法的语法调用get_friends,即Player::get_friends(),编译器会给出如下错误提示信息:

这里的错误提示信息具有误导性,但它表明实例方法基本上就是关联方法,self 是第 1 个参数,而 instance.foo()是一种语法糖。这意味着我们可以这样调用它:Player::get_friends (&player);。在此调用中,我们给方法传递了一个 Player 的实例,&self就是&player。
我们可以在类型上实现 3 种实例方法的变体。
self作为第一个参数。在这种情况下,调用此方法将不允许你后续使用该类型。&self作为第一个参数。此方法仅提供对类型实例的读取访问权限。&mut self作为第一个参数。此方法提供对类型实例的可变访问。
我们的 set_friends 方法是一个&mut self 方法,它允许我们修改 player 中的字段。我们 需要在 self 之前添加运算符&,这表示 self 在方法存续期间被借用,这正是我们想要的。 如果没有&符号,调用者会将所有权移动到方法,这意味着在 get_friends 方法返回后将取消分配值,我们将不能再使用 Player 实例。
&self 参数
在 Rust 中,&self参数是一种方法定义的方式,用来表明这个方法是某个类型的实例方法。
self代表实例本身的一个引用。这意味着当你在这个方法中使用self时,你实际上是在访问这个类型实例的数据。&符号表示这是一个借用(borrow),而不是所有权的转移。这样,当你调用这个方法时,实例的所有权不会被移动(move),允许在方法调用后继续使用该实例
&self使得你能够在方法内部访问实例的字段和其他方法,而不需要获取其所有权。这种方式非常适合于那些只需要读取实例数据,而不需要修改它的场景。
struct Person {name: String,}impl Person {fn consume(self) -> String {self.name}}fn main() {let person = Person { name: String::from("Alice") };let name = person.consume();// `person` 的所有权被 `consume` 方法获取// 这里 `person` 不能再被使用,因为它的所有权已经在 `consume` 方法中被消耗println!("Name is: {}", name);}
