Rust 面向对象

Rust 没有 class:用结构体封装数据、trait 做接口、组合代替继承。

Advanced
Key PointsRust 支持封装和多态,但不支持继承——因为继承的复用和扩展可以用组合 + trait 达成。

封装

Rust
pub struct Account {
    owner: String,          // 私有
    balance: u64,           // 私有
}

impl Account {
    pub fn new(owner: &str, initial: u64) -> Self {
        Account { owner: owner.into(), balance: initial }
    }

    pub fn balance(&self) -> u64 {
        self.balance
    }

    pub fn deposit(&mut self, amount: u64) {
        self.balance += amount;
    }

    pub fn withdraw(&mut self, amount: u64) -> Result<(), String> {
        if amount > self.balance {
            return Err(format!("余额不足,当前 {}", self.balance));
        }
        self.balance -= amount;
        Ok(())
    }
}

fn main() {
    let mut a = Account::new("张三", 100);
    a.deposit(50);
    println!("{}", a.balance());
    println!("{:?}", a.withdraw(500));
}
Output
150
Err("余额不足,当前 150")

Fields are private by default; only pub ones are visible. External code **cannot touch balance** and must go through deposit/withdraw — encapsulation without getter/setter boilerplate.

trait 对象

Rust
trait Draw {
    fn draw(&self) -> String;
}

struct Button { label: String }
struct TextBox { text: String }

impl Draw for Button {
    fn draw(&self) -> String { format!("[{}]", self.label) }
}

impl Draw for TextBox {
    fn draw(&self) -> String { format!("<{}>", self.text) }
}

fn main() {
    let widgets: Vec<Box<dyn Draw>> = vec![
        Box::new(Button { label: "确定".into() }),
        Box::new(TextBox { text: "输入".into() }),
    ];

    for w in &widgets {
        println!("{}", w.draw());
    }
}
Output
[确定]
<输入>

Box<dyn Draw> means "any type implementing Draw", dispatched through a vtable at runtime — Rust's answer to an array of base-class pointers.

状态模式

Rust
trait State {
    fn next(self: Box<Self>) -> Box<dyn State>;
    fn name(&self) -> &str;
}

struct Draft;
struct Review;
struct Published;

impl State for Draft {
    fn next(self: Box<Self>) -> Box<dyn State> { Box::new(Review) }
    fn name(&self) -> &str { "草稿" }
}

impl State for Review {
    fn next(self: Box<Self>) -> Box<dyn State> { Box::new(Published) }
    fn name(&self) -> &str { "审核中" }
}

impl State for Published {
    fn next(self: Box<Self>) -> Box<dyn State> { Box::new(Published) }
    fn name(&self) -> &str { "已发布" }
}

struct Post { state: Box<dyn State> }

impl Post {
    fn new() -> Self { Post { state: Box::new(Draft) } }
    fn advance(&mut self) {
        self.state = self.state.next();   // 需要移动,所以用 Box<Self>
    }
    fn status(&self) -> &str { self.state.name() }
}

fn main() {
    let mut p = Post::new();
    println!("{}", p.status());
    p.advance();
    println!("{}", p.status());
    p.advance();
    println!("{}", p.status());
}
Output
草稿
审核中
已发布
fn next(self: Box<Self>) is "boxed self", which consumes the old state and enforces one-way transitions. With &self the old state could still be reused.

用枚举代替状态类

Rust
// 很多时候枚举比 trait 对象更简单
#[derive(Debug, PartialEq)]
enum State {
    Draft,
    Review,
    Published,
}

struct Post {
    state: State,
    content: String,
}

impl Post {
    fn advance(&mut self) {
        self.state = match self.state {
            State::Draft => State::Review,
            State::Review => State::Published,
            State::Published => State::Published,
        };
    }
}

fn main() {
    let mut p = Post { state: State::Draft, content: String::new() };
    p.advance();
    println!("{:?}", p.state);
}
Output
Review

Use an enum when the state set is closed: the compiler forces you to handle every variant and adding one flags every match. Use trait objects when states must be open for extension by third parties.

没有继承,怎么复用

GoalOOP wayRust way
复用实现继承父类组合 + 委托
共享接口抽象基类trait
运行时多态虚函数dyn Trait
编译期多态模板泛型 + trait bound
默认实现父类方法trait 默认方法

Friends & Partners