没有行为的类型功能有限,并且通常情况下我们希望类型具有函数或方法,以便我们可以返回它们的实例而不是手动构造它们,或者使我们能够操作自定义类型中的字段。这可以通过 impl块来实现,它被视作某个类型提供实现。我们可以为所有自定义类型或包装器提供实现。

结构体上的 impl 块

我们可以使用两种机制向之前定义的结构体 Player 中添加行为:一种是类似构造函数的函数,它接收一个名称并为 Person 中的其余字段设置默认值,另一种是设置 Personfriends 字段的 gettersetter 方法。

  1. struct Player {
  2. name: String,
  3. iq: u8,
  4. friends: u8
  5. }
  6. impl Player {
  7. fn with_name(name: &str) -> Player {
  8. Player {
  9. name: name.to_string(),
  10. iq: 100,
  11. friends: 100
  12. }
  13. }
  14. fn get_friends(&self) -> u8 {
  15. self.friends
  16. }
  17. fn set_friends(&mut self,count: u8){
  18. self.friends = count;
  19. }
  20. }
  21. fn main() {
  22. let mut player = Player::with_name("Dave");
  23. player.set_friends(23);
  24. println!("{}'s friends count: {}",player.name,player.get_friends());
  25. //另一种调用实例方法的方式
  26. let _ = Player::get_friends(&player);
  27. }
  1. 我们指定关键字`impl`,然后指定我们希望实现方法的类型,后跟一对花括号。在花括号中,我们可以编写两种方法:
  • 关联方法:
    • 该方法没有self类型作为第1个参数。with_name方法被称为关联方法,因为它没有使用self作为第1个参数。它类似于面向对象编程语言中的静态方法。这些方法的类型自身上即可调用,并且不需要类型的实例来调用。通过在方法名称前加上结构体名称和双冒号来调用关联方法,如下所示:
    • Player::with_name("Dave");
  • 实例方法:
    • self作为第1个参数的函数。这里的self类似于Python 中的 self,并指向实现该方法的实例(这里是 Player)。因此,get_friends()方法只能在已创建的结构体上实例调用:
  1. let mut player = Player::with_name("Dave");
  2. player.set_friends(23);

如果我们使用关联方法的语法调用get_friends,即Player::get_friends(),编译器会给出如下错误提示信息:

类型上的函数和方法 - 图1

这里的错误提示信息具有误导性,但它表明实例方法基本上就是关联方法,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使得你能够在方法内部访问实例的字段和其他方法,而不需要获取其所有权。这种方式非常适合于那些只需要读取实例数据,而不需要修改它的场景。

  1. struct Person {
  2. name: String,
  3. }
  4. impl Person {
  5. fn consume(self) -> String {
  6. self.name
  7. }
  8. }
  9. fn main() {
  10. let person = Person { name: String::from("Alice") };
  11. let name = person.consume();
  12. // `person` 的所有权被 `consume` 方法获取
  13. // 这里 `person` 不能再被使用,因为它的所有权已经在 `consume` 方法中被消耗
  14. println!("Name is: {}", name);
  15. }