设想一个场景,我们定义了很多的结构,但是之前都只是为了储存一些静态数据,那能不能通过添加一些属性,添加的属性值是一个方法,该方法用于返回一个跟结构本身参数相关联的计算结果呢?
是可以的,废话少说先上示例:
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn get_perimeter(&self) -> u32 {
(self.width + self.height) * 2
}
}
fn main() {
let rect1 = Rectangle {
width: 36,
height: 18,
};
println!("矩形:{:?}的矩形的周长为:{}", rect1, rect1.get_perimeter())
}
cargo run
矩形:Rectangle { width: 36, height: 18 }的矩形的周长为:108
为了在Rectangle的上下文中定义函数,我们启动一个impl(实现)块。然后,我们将区域函数移动到impl大括号内,并将第一个(在这种情况下,仅此参数)参数更改为签名中和正文中各处的self。在main中,我们调用了get_perimeter函数并传递了rect1作为参数,我们可以改用方法语法在Rectangle实例上调用get_perimeter方法。方法语法在实例之后:我们在方法名称,括号和任何参数后面添加一个点。
在区域签名中,我们使用&self而不是矩形:&Rectangle,因为Rust知道self的类型为Rectangle,因为该方法位于impl Rectangle上下文中。请注意,就像在&Rectangle中一样,我们仍需要在self之前使用&。方法可以取得自我的所有权,像我们在这里所做的那样,不可变地借用自我,或者其本身是可变的去借用自我,就像它们可以使用任何其他参数一样。
我们在此处选择&self的原因与在函数版本中使用&Rectangle的原因相同:我们不想获得所有权,我们只想读取结构中的数据,而不是对其进行写入。如果要在方法执行的过程中更改调用方法的实例,则可以使用&mut self作为第一个参数。很少有一种方法只使用self作为第一个参数来获取实例的所有权;该方法通常在方法将自身转换为其他形式并且要防止调用者在转换后使用原始实例时使用。
除了使用方法语法而且不必在每个方法的签名中重复使用self的类型之外,使用方法代替函数的主要好处是对程序结构而言。我们将类型实例的所有功能都放在一个impl块中,而不是让我们的代码的未来用户在我们提供的库中的各个位置搜索Rectangle的功能。
一个struct可以有多个impl
有很多参数情况的方法
让我们通过在Rectangle结构上实现第二种方法来练习使用方法。 这次,我们希望Rectangle的一个实例接受Rectangle的另一个实例,如果第二个Rectangle可以完全适合自身(宽度是长度的2倍),则返回true。 否则应返回false。 也就是说,一旦定义了can_hold方法,我们希望能够编写下面示例所代表的程序:
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn get_perimeter(&self) -> u32 {
(self.width + self.height) * 2
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle {
width: 36,
height: 18,
};
let rect2 = Rectangle {
width: 100,
height: 18,
};
let rect3 = Rectangle {
width: 12,
height: 6,
};
println!("矩形rect1:{:?}可以容纳矩形rect2:{:?}吗? {}", rect1, rect2, rect1.can_hold(&rect2));
println!("矩形rect1:{:?}可以容纳矩形rect3:{:?}吗? {}", rect1, rect3, rect1.can_hold(&rect3));
}
cargo run
|
8 | fn get_perimeter(&self) -> u32 {
| ^^^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: 1 warning emitted
Finished dev [unoptimized + debuginfo] target(s) in 0.53s
Running `target\debug\cargo_learn.exe`
矩形rect1:Rectangle { width: 36, height: 18 }可以容纳矩形rect2:Rectangle { width: 100, height: 18 }吗? false
矩形rect1:Rectangle { width: 36, height: 18 }可以容纳矩形rect3:Rectangle { width: 12, height: 6 }吗? true
相关功能
impl块的另一个有用功能是允许我们在不以self为参数的impl块中定义函数。 这些之所以称为关联函数,是因为它们与struct相关联。 它们仍然是函数,而不是方法,因为它们没有可使用的结构实例。再次之前我们已经使用了String::from关联函数。
关联函数通常用于将返回该结构的新实例的构造函数。 例如,我们可以提供一个关联的函数,该函数将具有一个维度参数并将其用作宽度和高度,从而使创建正方形矩形变得更加容易,而不必两次指定相同的值:
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
fn main() {
let sq = Rectangle::square(3);
}
要调用此关联函数,我们将::语法与结构名称一起使用; let sq = Rectangle::square(3); 是一个实例。 该函数借由struct的命名空间:::语法用于关联函数和模块创建的命名空间。