【Rust 基础入门】(11) | 方法

  • 0xE
  • 发布于 3天前
  • 阅读 254

本文通过飞船示例介绍了Rust中的方法,展示了如何用impl为结构体和枚举定义行为,包括实例方法、关联函数和同名方法,突出其与普通函数的区别及灵活性。

在前面的章节中,我们学会了用结构体和枚举定义数据的“形状”。但光有静态属性还不够,程序的活力来自于行为。比如,一个游戏角色需要跳跃、攻击,一个计算器需要加减运算。在 Rust 中,这些行为通过方法来实现。方法是为特定类型定义的函数,让你的数据“动”起来。

想象你在玩一款太空探险游戏,驾驶一艘飞船。飞船有燃料量和速度这些属性,但如果没有“加速”或“减速”的动作,它就只是个摆设。方法就像飞船的控制按钮,让它真正飞起来。接下来,我们通过例子一步步看看 Rust 中的方法是如何工作的。


方法是什么?

方法是与结构体(struct)、枚举(enum)或特征(trait)关联的特殊函数。它们通过 impl 关键字定义,第一个参数通常是 &self ,表示调用这个方法的实例。方法可以用点号(.)调用,类似其他语言中的对象方法。

Rust 的方法有几个关键特点:

  • 与类型绑定:方法属于某个特定类型,而不是独立存在。
  • 借用实例:通过 &self,方法可以安全访问实例的数据。
  • 灵活调用:既能操作实例,也能定义不依赖实例的“关联函数”。

你可能好奇:方法和普通函数有什么区别?简单来说,方法绑定到类型,在 impl 块中定义,靠 &self 访问实例,用.调用;而函数独立存在,不需要实例,直接用函数名调用。让我们从一个例子入手,感受一下。


为飞船定义方法

假设我们要设计一艘飞船,用结构体表示它的状态,并添加一些行为。

代码

struct Spaceship {
    fuel: u32,  // 燃料量
    speed: u32,  // 当前速度
}

impl Spaceship {
    // 方法:加速,增加速度并消耗燃料
    fn accelerate(&mut self) {
        if self.fuel > 0 {
            self.speed += 10;
            self.fuel -= 5;
        }
    }

    // 方法:检查剩余燃料
    fn fuel_left(&self) -> u32 {
        self.fuel
    }
}

fn main() {
    let mut ship = Spaceship { fuel: 100, speed: 0 };
    println!("初始速度: {}, 燃料: {}", ship.speed, ship.fuel_left());
    ship.accelerate();
    println!("加速后速度: {}, 燃料: {}", ship.speed, ship.fuel_left());
}

输出

初始速度: 0, 燃料: 100
加速后速度: 10, 燃料: 95

这里,impl Spaceship 为飞船定义了方法。fuel_left 用 &self,只读访问实例;而 accelerate 用 &mut self,因为它修改了 speed 和 fuel,需要可变借用。你可能会问:为什么用 &self 而不是直接用 self ?答案是 &self 只借用实例,调用后实例还能继续使用。如果用 self ,实例会被移动,调用后就没法用了,这不符合大多数场景的需求。通过.调用方法,像 ship.accelerate() 这样,既直观又安全。


关联函数——创建实例的便捷方式

除了操作实例的方法,Rust 还支持“关联函数”。它们不依赖 self ,通常用来构造实例,用 :: 调用。你可以把它们看作类型的“静态方法”,特别适合初始化对象。

代码

struct Spaceship {
    fuel: u32,
    speed: u32,
}

impl Spaceship {
    // 关联函数:创建一艘新飞船
    fn new(fuel: u32) -> Spaceship {
        Spaceship { fuel, speed: 0 }
    }

    // 方法:报告状态
    fn status(&self) {
        println!("飞船状态 - 燃料: {}, 速度: {}", self.fuel, self.speed);
    }
}

fn main() {
    let ship = Spaceship::new(50);  // 用关联函数创建实例
    ship.status();
}

输出

飞船状态 - 燃料: 50, 速度: 0

new 是个关联函数,没有 self 参数,用 Spaceship::new(50) 调用,返回一个新飞船。相比直接写 Spaceship { fuel: 50, speed: 0 } ,它更简洁,也更符合封装的习惯。关联函数和普通方法不同,它不操作某个实例,而是类型本身的工具函数,常用于创建对象或执行类型相关的操作。


同名方法——简化字段访问

有时候,我们希望用方法直接访问字段,甚至让方法名和字段名相同,这样代码更直观。

代码

struct Spaceship {
    fuel: u32,
    speed: u32,
}

impl Spaceship {
    // 同名方法:获取速度
    fn speed(&self) -> u32 {
        self.speed
    }
}

fn main() {
    let ship = Spaceship { fuel: 80, speed: 15 };
    println!("直接访问字段: {}", ship.speed);  // 访问字段
    println!("调用同名方法: {}", ship.speed());  // 调用方法
}

输出

直接访问字段: 15
调用同名方法: 15

这里 speed 方法和字段同名,返回 self.speed 。这有什么好处呢?一是调用 ship.speed() 比直接用 ship.speed 更统一,像个“getter”;二是未来可以在方法里加逻辑(比如日志),而调用代码不用改。虽然结果一样,但方法提供了更多灵活性。


枚举上的方法

方法不仅适用于结构体,也可以用在枚举上。假设我们定义飞船的“指令”:

代码

enum Command {
    Launch,  // 起飞
    Land,  // 降落
    Report(String),  // 报告信息
}

impl Command {
    fn execute(&self) {
        match self {
            Command::Launch => println!("飞船起飞!"),
            Command::Land => println!("飞船降落!"),
            Command::Report(msg) => println!("报告: {}", msg),
        }
    }
}

fn main() {
    let cmd1 = Command::Launch;
    let cmd2 = Command::Report(String::from("燃料充足"));
    cmd1.execute();
    cmd2.execute();
}

输出

飞船起飞!
报告: 燃料充足

枚举方法的定义和结构体差不多,只不过通常用 match 处理不同变体。execute 根据指令类型执行不同行为,展示了方法的多样性。


总结

方法是 Rust 中为类型赋予行为的核心工具。通过 impl 块,你可以定义实例方法(如accelerate)和关联函数(如new)。无论在结构体还是枚举上,方法都让你的代码更灵活、更具表现力。它们和普通函数的区别在于绑定类型和 &self,而关联函数则提供了额外的创建便利。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
0xE
0xE
0x59f6...a17e
17年进入币圈,Web3 开发者。刨根问底探链上真相,品味坎坷悟 Web3 人生。有工作机会可加v:__0xE__