Inheritance vs Composition | 继承 vs 组合
What you’ll learn: Why Rust has no class inheritance, how traits + structs replace deep class hierarchies, and practical patterns for achieving polymorphism through composition.
你将学到什么: 为什么 Rust 没有类继承,trait + struct 如何取代深层类层次结构, 以及如何通过组合实现多态的实用模式。
Difficulty: Intermediate
难度: 中级
// C# - Class-based inheritance
public abstract class Animal
{
public string Name { get; protected set; }
public abstract void MakeSound();
public virtual void Sleep()
{
Console.WriteLine($"{Name} is sleeping");
}
}
public class Dog : Animal
{
public Dog(string name) { Name = name; }
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
public void Fetch()
{
Console.WriteLine($"{Name} is fetching");
}
}
// Interface-based contracts
public interface IFlyable
{
void Fly();
}
public class Bird : Animal, IFlyable
{
public Bird(string name) { Name = name; }
public override void MakeSound()
{
Console.WriteLine("Tweet!");
}
public void Fly()
{
Console.WriteLine($"{Name} is flying");
}
}
Rust Composition Model | Rust 的组合模型
#![allow(unused)]
fn main() {
// Rust - Composition over inheritance with traits
pub trait Animal {
fn name(&self) -> &str;
fn make_sound(&self);
// Default implementation (like C# virtual methods)
fn sleep(&self) {
println!("{} is sleeping", self.name());
}
}
pub trait Flyable {
fn fly(&self);
}
// Separate data from behavior
#[derive(Debug)]
pub struct Dog {
name: String,
}
#[derive(Debug)]
pub struct Bird {
name: String,
wingspan: f64,
}
// Implement behaviors for types
impl Animal for Dog {
fn name(&self) -> &str {
&self.name
}
fn make_sound(&self) {
println!("Woof!");
}
}
impl Dog {
pub fn new(name: String) -> Self {
Dog { name }
}
pub fn fetch(&self) {
println!("{} is fetching", self.name);
}
}
impl Animal for Bird {
fn name(&self) -> &str {
&self.name
}
fn make_sound(&self) {
println!("Tweet!");
}
}
impl Flyable for Bird {
fn fly(&self) {
println!("{} is flying with {:.1}m wingspan", self.name, self.wingspan);
}
}
// Multiple trait bounds (like multiple interfaces)
fn make_flying_animal_sound<T>(animal: &T)
where
T: Animal + Flyable,
{
animal.make_sound();
animal.fly();
}
}
graph TD
subgraph "C# Inheritance Hierarchy"
CS_ANIMAL["Animal (abstract class)"]
CS_DOG["Dog : Animal"]
CS_BIRD["Bird : Animal, IFlyable"]
CS_VTABLE["Virtual method dispatch<br/>Runtime cost"]
CS_COUPLING["[ERROR] Tight coupling<br/>[ERROR] Diamond problem<br/>[ERROR] Deep hierarchies"]
CS_ANIMAL --> CS_DOG
CS_ANIMAL --> CS_BIRD
CS_DOG --> CS_VTABLE
CS_BIRD --> CS_VTABLE
CS_ANIMAL --> CS_COUPLING
end
subgraph "Rust Composition Model"
RUST_ANIMAL["trait Animal"]
RUST_FLYABLE["trait Flyable"]
RUST_DOG["struct Dog"]
RUST_BIRD["struct Bird"]
RUST_IMPL1["impl Animal for Dog"]
RUST_IMPL2["impl Animal for Bird"]
RUST_IMPL3["impl Flyable for Bird"]
RUST_STATIC["Static dispatch<br/>Zero cost"]
RUST_FLEXIBLE["[OK] Flexible composition<br/>[OK] No hierarchy limits<br/>[OK] Mix and match traits"]
RUST_DOG --> RUST_IMPL1
RUST_BIRD --> RUST_IMPL2
RUST_BIRD --> RUST_IMPL3
RUST_IMPL1 --> RUST_ANIMAL
RUST_IMPL2 --> RUST_ANIMAL
RUST_IMPL3 --> RUST_FLYABLE
RUST_IMPL1 --> RUST_STATIC
RUST_IMPL2 --> RUST_STATIC
RUST_IMPL3 --> RUST_STATIC
RUST_ANIMAL --> RUST_FLEXIBLE
RUST_FLYABLE --> RUST_FLEXIBLE
end
style CS_COUPLING fill:#ffcdd2,color:#000
style RUST_FLEXIBLE fill:#c8e6c9,color:#000
style CS_VTABLE fill:#fff3e0,color:#000
style RUST_STATIC fill:#c8e6c9,color:#000
Rust 并不是“不能做面向对象”,而是把复用和多态从“继承层次”转成了“能力组合”。
Exercises | 练习
Exercise: Replace Inheritance with Traits | 练习:用 Trait 替代继承 (click to expand / 点击展开)
This C# code uses inheritance. Rewrite it in Rust using trait composition:
下面这段 C# 代码使用了继承。请用 Rust 的 trait 组合方式重写它:
public abstract class Shape { public abstract double Area(); }
public abstract class Shape3D : Shape { public abstract double Volume(); }
public class Cylinder : Shape3D
{
public double Radius { get; }
public double Height { get; }
public Cylinder(double r, double h) { Radius = r; Height = h; }
public override double Area() => 2.0 * Math.PI * Radius * (Radius + Height);
public override double Volume() => Math.PI * Radius * Radius * Height;
}
Requirements:
HasAreatrait withfn area(&self) -> f64HasVolumetrait withfn volume(&self) -> f64Cylinderstruct implementing both- A function
fn print_shape_info(shape: &(impl HasArea + HasVolume))- note the trait bound composition (no inheritance needed)
要求:
- 定义
HasAreatrait,包含fn area(&self) -> f64 - 定义
HasVolumetrait,包含fn volume(&self) -> f64 - 创建同时实现两者的
Cylinder结构体 - 编写函数
fn print_shape_info(shape: &(impl HasArea + HasVolume))注意这里是 trait bound 的组合,不需要继承
Solution | 参考答案
use std::f64::consts::PI;
trait HasArea {
fn area(&self) -> f64;
}
trait HasVolume {
fn volume(&self) -> f64;
}
struct Cylinder {
radius: f64,
height: f64,
}
impl HasArea for Cylinder {
fn area(&self) -> f64 {
2.0 * PI * self.radius * (self.radius + self.height)
}
}
impl HasVolume for Cylinder {
fn volume(&self) -> f64 {
PI * self.radius * self.radius * self.height
}
}
fn print_shape_info(shape: &(impl HasArea + HasVolume)) {
println!("Area: {:.2}", shape.area());
println!("Volume: {:.2}", shape.volume());
}
fn main() {
let c = Cylinder { radius: 3.0, height: 5.0 };
print_shape_info(&c);
}
Key insight: C# needs a 3-level hierarchy (Shape -> Shape3D -> Cylinder). Rust uses flat trait composition - impl HasArea + HasVolume combines capabilities without inheritance depth.
关键理解: C# 需要三层继承结构(Shape -> Shape3D -> Cylinder),而 Rust 用平铺的 trait 组合即可:impl HasArea + HasVolume 直接把能力组合起来,不需要继承深度。