Rust入门开发之Rust中如何实现面向对象编程

前言

Rust虽然不是纯粹的面向对象编程语言(OOP),但它支持面向对象的核心概念:封装、继承和多态。Rust通过结构体/枚举、特性(Traits)和特性对象实现了灵活且安全的面向对象编程范式。

一、封装(Encapsulation)

封装指隐藏对象内部实现细节,只暴露必要的接口。Rust通过pub关键字控制访问权限,实现封装:

rust 复制代码
pub struct BankAccount {
    account_number: String, // 私有字段:默认不可访问
    balance: f64, // 私有字段
}

impl BankAccount {
    // 公共构造函数
    pub fn new(account_number: String) -> Self {
        BankAccount {
            account_number,
            balance: 0.0,
        }
    }

    // 公共方法:存款
    pub fn deposit(&mut self, amount: f64) {
        if amount > 0.0 {
            self.balance += amount;
        }
    }

    // 公共方法:取款
    pub fn withdraw(&mut self, amount: f64) -> Result<(), String> {
        if amount <= 0.0 {
            return Err(String::from("Amount must be positive"));
        }
        if amount > self.balance {
            return Err(String::from("Insufficient funds"));
        }
        self.balance -= amount;
        Ok(())
    }

    // 公共方法:查询余额
    pub fn balance(&self) -> f64 {
        self.balance
    }

    // 私有方法:日志记录(仅内部使用)
    fn log_transaction(&self, message: &str) {
        println!("[{}] {}", self.account_number, message);
    }
}

fn main() {
    let mut account = BankAccount::new(String::from("123456"));
    account.deposit(1000.0);
    println!("Balance: {}", account.balance()); // 输出:Balance: 1000

    match account.withdraw(500.0) {
        Ok(_) => println!("Withdrawal successful"),
        Err(e) => println!("Error: {}", e),
    }
}

在上述示例中:

  • 结构体的字段(account_numberbalance)是私有的,外部无法直接修改
  • 公共方法(depositwithdraw等)提供了受控的访问接口
  • 私有方法(log_transaction)隐藏了内部实现细节
二、继承(Inheritance)与代码复用

Rust不支持传统OOP中的继承(通过类层次结构共享代码),而是通过以下方式实现代码复用:

1. 组合(Composition)

"组合优于继承"是Rust的设计理念,通过结构体包含其他结构体实现功能复用:

rust 复制代码
struct Engine {
    horsepower: u32,
}

impl Engine {
    fn start(&self) {
        println!("Engine with {} HP started", self.horsepower);
    }
}

struct Car {
    engine: Engine, // 组合Engine结构体
    model: String,
}

impl Car {
    fn new(model: String, horsepower: u32) -> Self {
        Car {
            engine: Engine { horsepower },
            model,
        }
    }

    fn start(&self) {
        println!("Starting {}...", self.model);
        self.engine.start(); // 复用Engine的功能
    }
}

fn main() {
    let my_car = Car::new(String::from("Tesla Model 3"), 283);
    my_car.start();
    // 输出:
    // Starting Tesla Model 3...
    // Engine with 283 HP started
}
2. 特性默认实现

通过特性的默认方法实现代码复用,类似"接口继承":

rust 复制代码
trait Vehicle {
    fn start(&self) {
        println!("Vehicle starting..."); // 默认实现
    }

    fn stop(&self) {
        println!("Vehicle stopping..."); // 默认实现
    }
}

struct Car;
struct Bike;

// 实现特性时可直接使用默认方法
impl Vehicle for Car {}

// 也可重写默认方法
impl Vehicle for Bike {
    fn start(&self) {
        println!("Bike starting (pedal hard!)");
    }
}

fn main() {
    let car = Car;
    let bike = Bike;

    car.start(); // 输出:Vehicle starting...
    bike.start(); // 输出:Bike starting (pedal hard!)
}
三、 多态(Polymorphism)

多态指同一接口可以用于不同类型的对象。Rust通过特性对象(Trait Objects)实现动态多态:

rust 复制代码
// 定义"可绘制"特性
trait Drawable {
    fn draw(&self);
}

// 具体类型实现特性
struct Circle {
    radius: f64,
}

impl Drawable for Circle {
    fn draw(&self) {
        println!("Drawing a circle with radius {}", self.radius);
    }
}

struct Square {
    side_length: f64,
}

impl Drawable for Square {
    fn draw(&self) {
        println!("Drawing a square with side length {}", self.side_length);
    }
}

// 多态函数:接受任何可绘制的对象
fn render(drawable: &dyn Drawable) {
    drawable.draw();
}

fn main() {
    let circle = Circle { radius: 5.0 };
    let square = Square { side_length: 10.0 };

    render(&circle); // 输出:Drawing a circle with radius 5
    render(&square); // 输出:Drawing a square with side length 10
}

dyn Drawable是特性对象,表示"任何实现了Drawable特性的类型"。在运行时,render函数会根据实际类型调用对应的draw方法。

四、 动态分发与静态分发

Rust支持两种多态分发方式:

  1. 静态分发(通过泛型和特性约束):
rust 复制代码
// 静态分发:编译时确定具体类型,生成专门的代码
fn render_static<T: Drawable>(drawable: &T) {
    drawable.draw();
}

静态分发性能更好(无运行时开销),但会导致代码膨胀(为每种类型生成代码)。

  1. 动态分发(通过特性对象):
rust 复制代码
// 动态分发:运行时通过虚函数表(vtable)确定调用的方法
fn render_dynamic(drawable: &dyn Drawable) {
    drawable.draw();
}

动态分发更灵活(可在运行时混合不同类型),但有轻微的运行时开销。

五、 面向对象设计模式示例:状态模式

1、使用枚举和特性实现状态模式,封装对象在不同状态下的行为:

rust 复制代码
// 定义状态特性
trait State {
    fn on_enter(&self) {
        println!("Entering state");
    }
    fn handle_event(&self) -> Box<dyn State>;
    fn on_exit(&self) {
        println!("Exiting state");
    }
}

// 具体状态实现
struct StoppedState;
struct PlayingState;
struct PausedState;

impl State for StoppedState {
    fn handle_event(&self) -> Box<dyn State> {
        println!("Stopped: Playing...");
        Box::new(PlayingState)
    }
}

impl State for PlayingState {
    fn handle_event(&self) -> Box<dyn State> {
        println!("Playing: Pausing...");
        Box::new(PausedState)
    }
}

impl State for PausedState {
    fn handle_event(&self) -> Box<dyn State> {
        println!("Paused: Stopping...");
        Box::new(StoppedState)
    }
}

// 上下文对象:持有当前状态
struct Player {
    state: Box<dyn State>,
}

impl Player {
    fn new() -> Self {
        Player {
            state: Box::new(StoppedState),
        }
    }

    fn press_button(&mut self) {
        self.state.on_exit();
        self.state = self.state.handle_event();
        self.state.on_enter();
    }
}

fn main() {
    let mut player = Player::new();
    player.press_button(); // 从Stopped → Playing
    player.press_button(); // 从Playing → Paused
    player.press_button(); // 从Paused → Stopped
}

2、使用结构体和方法实现封装

rust 复制代码
struct Rectangle {
    width: u32,
    height: u32,
}

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

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

    fn set_width(&mut self, width: u32) {
        self.width = width;
    }
}
 

2、通过特质(trait)实现多态

rust 复制代码
trait Drawable {
    fn draw(&self);
}

struct Circle;
struct Square;

impl Drawable for Circle {
    fn draw(&self) {
        println!("Drawing a circle");
    }
}

impl Drawable for Square {
    fn draw(&self) {
        println!("Drawing a square");
    }
}

fn render(item: &impl Drawable) {
    item.draw();
}
 

3、使用枚举实现状态模式

rust 复制代码
enum TrafficLight {
    Red,
    Yellow,
    Green,
}

impl TrafficLight {
    fn next(&self) -> Self {
        match self {
            TrafficLight::Red => TrafficLight::Green,
            TrafficLight::Yellow => TrafficLight::Red,
            TrafficLight::Green => TrafficLight::Yellow,
        }
    }
}
 

4、组合优于继承的实现

rust 复制代码
struct Engine {
    horsepower: u32,
}

struct Car {
    engine: Engine,
    model: String,
}

impl Car {
    fn new(horsepower: u32, model: String) -> Self {
        Car {
            engine: Engine { horsepower },
            model,
        }
    }

    fn description(&self) -> String {
        format!("{} with {}hp engine", self.model, self.engine.horsepower)
    }
}
 

5、使用Box实现动态分发

rust 复制代码
trait Animal {
    fn speak(&self);
}

struct Dog;
struct Cat;

impl Animal for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

impl Animal for Cat {
    fn speak(&self) {
        println!("Meow!");
    }
}

fn animal_farm(animals: Vec<Box<dyn Animal>>) {
    for animal in animals {
        animal.speak();
    }
}
 
六、总结

Rust虽然不采用传统的类继承模型,但通过结构体/枚举的封装、特性的默认实现(代码复用)和特性对象(多态),提供了强大的面向对象编程能力。Rust的设计更强调组合优于继承,静态分发与动态分发结合,在保证灵活性的同时兼顾性能和安全性。这种方式避免了传统OOP中继承带来的复杂性,同时保留了面向对象的核心优势。

相关推荐
yq14682860903 小时前
C (统计二进制中“1“的个数)
c语言·开发语言·算法
Mos_x3 小时前
15.<Spring Boot 日志>
java·后端
mm-q29152227293 小时前
Java并发编程从入门到进阶 多场景实战
java·开发语言
William_cl3 小时前
【ASP.NET MVC 进阶】DataAnnotations 特性验证全解析:从基础到避坑,让数据校验像 “安检“ 一样靠谱
后端·asp.net·mvc
SimonKing4 小时前
你的项目还在用MyBatis吗?或许这个框架更适合你:Easy-Query
java·后端·程序员
nice_lcj5204 小时前
认识多线程:单例模式
java·开发语言·单例模式
货拉拉技术4 小时前
从代码到配置:如何用SQL配置实现数据核对
java·后端
是苏浙4 小时前
零基础入门C语言之数据在内存中的存储
c语言·开发语言
xuejianxinokok4 小时前
可能被忽略的 pgvector 各种坑
数据库·后端