Skip to main content

Rust 基础入门 - 第六章

定义方法

Rust 使用 impl 关键字来定义方法,方法是在结构体或枚举上定义的函数。

info

impl == implementation

#![allow(unused)]
fn main() {
struct Circle {
x: f64,
y: f64,
radius: f64,
}

impl Circle {
// new 是 Circle 的关联函数,因为它的第一个参数不是 self,且 new 并不是关键字
// 这种方法往往用于初始化当前结构体的实例
fn new(x: f64, y: f64, radius: f64) -> Circle {
Circle {
x: x,
y: y,
radius: radius,
}
}

// Circle 的方法,&self 表示借用当前的 Circle 结构体
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
}

和其他语言不一样的是,其他面向对象的语言,所有定义都在类中,而 Rust 中,方法的定义是在 impl 块中的,如下图所示。这样数据和使用分离的方式,灵活度更高。

rust-impl

第二个例子:

#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle { // impl 块
fn area(&self) -> u32 {
self.width * self.height
}
}

fn main() {
let rect1 = Rectangle {width: 30, height: 50};

println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}

在这个例子中,定义了一个“矩形”结构体,然后在 impl 块中定义了一个 area 方法,这个方法接收一个 &self 参数,这个参数是一个指向当前实例的引用,这样就可以在方法中使用 self 来访问当前实例的数据。

self、&self、&mut self

area 的接受参数中,我们使用 &self 替代 rectangle: &Rectangle&self 其实是 self: &Self 的简写(注意大小写)。在一个 impl 块内,Self 指代被实现方法的结构体类型,self 指代此类型的实例,换句话说,self 指代的是 Rectangle 结构体实例,这样的写法会让我们的代码简洁很多,而且非常便于理解:我们为哪个结构体实现方法,那么 self 就是指代哪个结构体的实例。

其中 self 有三种形式,其所有权不同:

  • self:值被移动,不可再使用
  • &self:值的不可变引用
  • &mut self:值的可变引用

在上面计算面积的例子中,传入的参数是 &self,这是因为我们只是想读取结构体中的数据,而不需要获取所有权和修改结构体中的数据,所以使用不可变引用即可。

带有多个参数的方法

#![allow(unused)]
struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}

fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}

fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 40,
};
let rect3 = Rectangle {
width: 60,
height: 45,
};

println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

关联函数

定义在 impl 中且没有 self 的函数被称之为关联函数: 因为它没有 self,因此它是一个函数而不是方法,它又在 impl 中,与结构体紧密关联,因此称为关联函数。

note

Rust 中有一个约定俗成的规则,使用 new 来作为构造器的名称,出于设计上的考虑,Rust 特地没有用 new 作为关键字

fn main() {
struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle {
fn new(w: u32, h: u32) -> Rectangle {
Rectangle {width: w, height: h}
}
}
}

因为是函数,所以不能用 . 的方式来调用,我们需要用 :: 来调用,例如 let sq = Rectangle::new(3, 3);。这个方法位于结构体的命名空间中::: 语法用于关联函数和模块创建的命名空间。

为枚举类型定义方法

也可以为枚举类型定义方法:

#![allow(unused)]
enum Message {
Quit,
Move {x: i32, y: i32},
Write(String),
ChangeColor(i32, i32, i32),
}

impl Message {
fn call(&self) {
// 在这里定义方法体
}
}

fn main() {
let m = Message::Write(String::from("hello"));
m.call();
}

参考资料