Rust 面向对象
Rust 没有 class:用结构体封装数据、trait 做接口、组合代替继承。
封装
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));
}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 对象
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());
}
}[确定]
<输入>Box<dyn Draw> means "any type implementing Draw", dispatched through a vtable at runtime — Rust's answer to an array of base-class pointers.
状态模式
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());
}草稿
审核中
已发布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.用枚举代替状态类
// 很多时候枚举比 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);
}ReviewUse 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.
没有继承,怎么复用
| Goal | OOP way | Rust way |
|---|---|---|
| 复用实现 | 继承父类 | 组合 + 委托 |
| 共享接口 | 抽象基类 | trait |
| 运行时多态 | 虚函数 | dyn Trait |
| 编译期多态 | 模板 | 泛型 + trait bound |
| 默认实现 | 父类方法 | trait 默认方法 |
封装
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));
}150
Err("余额不足,当前 150")字段默认私有,只有 pub 的才对外可见。这意味着**外部代码不能直接改 balance**,必须走 deposit / withdraw——这就是封装,而且不需要 getter/setter 模板代码。
trait 对象
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());
}
}[确定]
<输入>Box<dyn Draw> 是「任何实现了 Draw 的类型」,运行期通过虚表调用。这是 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());
}草稿
审核中
已发布fn next(self: Box<Self>) 这种写法叫「self 装箱」,目的是消费掉旧状态,强制状态只能单向流转。用 &self 就无法保证旧状态不再被使用。用枚举代替状态类
// 很多时候枚举比 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);
}Review状态集合封闭时用枚举:编译器会强制你处理每个变体,加状态时所有 match 都会报错提醒。状态需要开放扩展(第三方插件)时才用 trait 对象。
没有继承,怎么复用
| 目标 | 面向对象做法 | Rust 做法 |
|---|---|---|
| 复用实现 | 继承父类 | 组合 + 委托 |
| 共享接口 | 抽象基类 | trait |
| 运行时多态 | 虚函数 | dyn Trait |
| 编译期多态 | 模板 | 泛型 + trait bound |
| 默认实现 | 父类方法 | trait 默认方法 |