没有行为的类型功能有限,并且通常情况下我们希望类型具有函数或方法,以便我们可以返回它们的实例而不是手动构造它们,或者使我们能够操作自定义类型中的字段。这可以通过 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);
}