01-组合结构体
组合结构体 — Rust Design Patterns
2 分钟阅读
译文 · 基于 Rust Design Patterns
原文链接: https://rust-unofficial.github.io/patterns/patterns/structural/compose-structs.html
组合结构体
描述
有时大型结构体会给借用检查器带来问题——虽然字段可以独立借用,但有时整个结构体最终被一次性使用,从而阻止其他用途。一种解决办法是将结构体分解为若干较小的结构体,再将它们组合回原来的结构体。这样每个结构体可以单独借用,并具有更灵活的行为。
这往往也会在其他方面带来更好的设计:应用此设计模式常常会揭示更小的功能单元。
示例
下面是一个刻意构造的例子,说明借用检查器如何妨碍我们使用结构体:
struct Database {
connection_string: String,
timeout: u32,
pool_size: u32,
}
fn print_database(database: &Database) {
println!("Connection string: {}", database.connection_string);
println!("Timeout: {}", database.timeout);
println!("Pool size: {}", database.pool_size);
}
fn main() {
let mut db = Database {
connection_string: "initial string".to_string(),
timeout: 30,
pool_size: 100,
};
let connection_string = &mut db.connection_string;
print_database(&db);
*connection_string = "new string".to_string();
}
编译器会抛出如下错误:
let connection_string = &mut db.connection_string;
------------------------- mutable borrow occurs here
print_database(&db);
^^^ immutable borrow occurs here
*connection_string = "new string".to_string();
------------------ mutable borrow later used here
我们可以应用此设计模式,将 Database 重构为三个较小的结构体,从而解决借用检查问题:
| |
动机
当你有一个最终包含大量希望独立借用的字段的结构体时,此模式最有用,从而最终获得更灵活的行为。
优点
结构体分解让你可以绕过借用检查器的限制,并且往往能产生更好的设计。
缺点
它可能导致代码更冗长。有时,较小的结构体并不是好的抽象,于是我们最终得到更差的设计。那多半是一种“代码异味”,表明程序应当以某种方式重构。
讨论
在没有借用检查器的语言中不需要此模式,因此从这个意义上说它是 Rust 特有的。然而,把功能拆成更小的单元往往会使代码更清晰:这是软件工程中广为认可的原则,与语言无关。
此模式依赖于 Rust 的借用检查器能够彼此独立地借用字段。在示例中,借用检查器知道 a.b 与 a.c 是不同的,可以独立借用,而不会试图借用整个 a——否则此模式就毫无用处。