前言
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_number、balance)是私有的,外部无法直接修改 - 公共方法(
deposit、withdraw等)提供了受控的访问接口 - 私有方法(
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支持两种多态分发方式:
- 静态分发(通过泛型和特性约束):
rust
// 静态分发:编译时确定具体类型,生成专门的代码
fn render_static<T: Drawable>(drawable: &T) {
drawable.draw();
}
静态分发性能更好(无运行时开销),但会导致代码膨胀(为每种类型生成代码)。
- 动态分发(通过特性对象):
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中继承带来的复杂性,同时保留了面向对象的核心优势。