我必须立刻押注 Rust 之五🎲:impl 实例方法与关联函数

813 阅读3分钟

impl 基础概念

impl 是 "implementation" 的缩写,表示实现。impl 块允许你为一个结构体定义行为,比如函数和方法。它的语法如下:

struct Rectangle {
    width: u32,
    height: u32,
}

// 使用 impl 为 Rectangle 添加功能
impl Rectangle {
    // 方法和关联函数定义在这里
}

实例方法

我们可以使用 impl 为结构体定义方法。这些方法一般会用到 self 参数来访问结构体实例的数据。

示例:为 Rectangle 结构体定义一个计算面积的 area 方法。

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 定义一个计算面积的方法
    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());
}

代码解释:

  • &self:表示此方法会以不可变引用的方式接收实例。通过 self,可以访问结构体的 widthheight
  • rect1.area():使用结构体实例 rect1 调用方法 area,Rust 自动识别 self 代表 rect1

关联函数

关联函数的特点是它们不接收 self 参数。关联函数通常用于定义结构体的 构造函数 或其他无需实例的方法。

示例:为 Rectangle 结构体定义一个关联函数 square,创建一个宽高相等的矩形。

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, height: size }
    }
}

fn main() {
    let sq = Rectangle::square(30);
    println!("Square rectangle has width {} and height {}", sq.width, sq.height);
}

代码解释:

  • Rectangle::square(30):调用关联函数 square 创建一个新的 Rectangle 实例。
  • 关联函数通常通过 结构体名::函数名 语法调用。

self 关键字的用法

在方法中,self 可以有三种形式:

  • self:表示获取实例的所有权,方法中可以修改实例数据。
  • &self:以不可变引用方式访问实例,不能修改实例数据。
  • &mut self:以可变引用方式访问实例,允许修改实例数据。

示例:添加一个方法 resize 修改矩形的大小。

impl Rectangle {
    fn resize(&mut self, new_width: u32, new_height: u32) {
        self.width = new_width;
        self.height = new_height;
    }
}

fn main() {
    let mut rect1 = Rectangle { width: 30, height: 50 };
    rect1.resize(60, 80);
    println!("Resized rectangle: width {}, height {}", rect1.width, rect1.height);
}

练习

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 一个关联函数
    fn new(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }

    // 一个结构体方法
    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::new(30, 50);  // 调用关联函数
    let rect2 = Rectangle::new(10, 20);

    println!("Area of rect1: {}", rect1.area());  // 调用结构体方法
    println!("rect1 can hold rect2: {}", rect1.can_hold(&rect2));
}

Q & A

Q: 关联函数与实例方法,差异就在 self 上吗?

A: 是的,关联函数和实例方法的主要差异就在于 self 的使用。

实例方法:接收 self,可以直接访问结构体的字段,强调了与具体实例的绑定

关联函数:不接收 self,用于定义不依赖于特定实例的函数,通常用于构造和静态逻辑。

Q: let area1 = Rectangle::area(&rect); 实例方法也可以通过 结构体名::方法(&实例) 的形式调用, 为什么如此设计?

A: Rust 在设计中追求一致性与对称性。Rust 的方法调用本质上是对 函数调用 的一种封装,将 实例.方法() 形式与 结构体名::方法(&实例) 形式视为对同一方法的不同调用方式。

可以在不同的实例上重复使用该方法,非常适合于通用代码(比如高阶函数或泛型函数).

// 这个函数接受一个闭包或函数作为参数,并调用它来获取面积
fn print_area<F>(rectangle: &Rectangle, area_func: F)
where
    F: Fn(&Rectangle) -> u32,
{
    let area = area_func(rectangle); // 调用传入的函数计算面积
    println!("The area of the rectangle is: {}", area);
}

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

    // 传入 `Rectangle::area` 作为参数,而不是直接调用 `rect.area()`
    print_area(&rect, Rectangle::area);
}

Rectangle::area 的这种形式可以在不绑定具体实例的情况下引用方法。