组合
本课属于 Coddy Rust 学习旅程的“面向对象编程”单元。第 55 课,共 61 课。
许多面向对象语言都大量依赖继承,在不同类型之间共享功能。Rust 采用了不同的方法:组合。你不从父类继承行为,而是将其他类型作为字段包含进来,从而构建复杂类型。
考虑对汽车进行建模。你可以不创建基础 Vehicle 类并从中继承,而是用更小、更专注的组件组合出一个 Car:
struct Engine {
horsepower: u32,
}
struct Wheels {
count: u8,
diameter: f32,
}
struct Car {
engine: Engine,
wheels: Wheels,
brand: String,
}
Car 不继承自 Engine 或 Wheels。它拥有它们。这种“拥有”关系就是组合。你可以通过字段访问内部组件,并直接调用它们的方法:
impl Engine {
fn start(&self) {
println!("Engine with {} HP started!", self.horsepower);
}
}
impl Car {
fn start(&self) {
self.engine.start(); // 委托给内部组件处理
}
}
这种模式让每个结构体都专注于单一职责。需要更改引擎的工作方式吗?只需修改 Engine 结构体。想在摩托车中复用 Wheels 吗?只需将它包含在新的结构体中。组合让你拥有灵活性,同时避免了继承通常会造成的紧密耦合。
挑战
简单让我们用组合来构建一个计算机系统!你不必创建一个庞大的结构体,而是用较小且职责明确的组件来组合出一个 Computer:一个 CPU 和一个 Memory。每个组件都能描述自身,而 Computer 会把工作委托给它的各个组成部分。
你需要将代码分布在三个文件中:
cpu.rs:定义一个公开的CPU结构体,其中有两个公开字段:cores(u32),表示核心数;speed_ghz(f32),表示时钟速度。添加一个specs方法,返回一个描述 CPU 的 String,格式为{cores}-core @ {speed_ghz}GHz。memory.rs:定义一个公开的Memory结构体,其中有一个公开字段size_gb(u32),表示内存大小。添加一个specs方法,返回格式为{size_gb}GB RAM的 String。main.rs:引入这两个模块,并创建一个公开的Computer结构体,它包含一个类型为CPU的cpu字段,以及一个类型为Memory的memory字段。在 Computer 上实现一个system_info方法,将工作委托给各个组件,并打印完整的系统规格。使用提供的输入来构建一个 Computer,并显示其信息。
system_info 方法应打印:
System: {cpu_specs} | {memory_specs}例如,输入 8、3.5 和 16 时:
System: 8-core @ 3.5GHz | 16GB RAM输入 4、2.8 和 32 时:
System: 4-core @ 2.8GHz | 32GB RAM你会收到三个输入:CPU 核心数(解析为 u32)、CPU 速度(解析为 f32)和内存大小(解析为 u32)。
亲手试试
mod cpu;
mod memory;
use cpu::CPU;
use memory::Memory;
// TODO:定义一个包含两个公共字段的公共 Computer 结构体:
// - cpu: CPU
// - memory: Memory
// TODO:为 Computer 实现一个 system_info 方法,使其:
// - 调用 cpu.specs() 和 memory.specs()
// - 打印:System: {cpu_specs} | {memory_specs}
fn main() {
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let cores: u32 = input.trim().parse().expect("Invalid number");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let speed_ghz: f32 = input.trim().parse().expect("Invalid number");
input.clear();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let size_gb: u32 = input.trim().parse().expect("Invalid number");
// TODO:使用解析后的输入创建 CPU 和 Memory 实例
// TODO:使用 CPU 和 Memory 创建一个 Computer 实例
// TODO:在 computer 上调用 system_info(),打印结果
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Rust 在线编译器