设计权衡
本节是关于 Rust 中的 设计权衡。要成为一名高效的 Rust 工程师,仅仅了解 Rust 的工作原理是不够的。您必须决定 Rust 的众多工具中哪些适合给定的工作。在本节中,我们将为您提供一系列关于您对 Rust 中设计权衡的理解的测验。每次测验后,我们将深入解释我们每个问题的理由。
这是一个问题示例。它将首先描述一个软件案例研究,其中包含一系列设计方案
背景: 您正在设计一个具有全局配置的应用程序,例如包含命令行标志。
功能: 应用程序需要在整个应用程序中传递对该配置的不可变引用。
设计方案: 以下是实现该功能的几种建议设计方案。
use std::rc::Rc; use std::sync::Arc; struct Config { flags: Flags, // .. more fields .. } // Option 1: use a reference struct ConfigRef<'a>(&'a Config); // Option 2: use a reference-counted pointer struct ConfigRef(Rc<Config>); // Option 3: use an atomic reference-counted pointer struct ConfigRef(Arc<Config>);
仅给定背景和关键功能,所有三种设计方案都是潜在的候选方案。我们需要更多关于系统目标的信息来决定哪种方案最有意义。因此,我们给出一个新的要求
选择满足以下要求的每个设计选项
要求: 配置引用必须在多个线程之间共享。
答案
选项 1
选项 2
选项 3
用正式术语来说,这意味着 ConfigRef
实现了 Send
和 Sync
。假设 Config: Send + Sync
,那么 &Config
和 Arc<Config>
都满足此要求,但 Rc
不满足(因为非原子引用计数指针不是线程安全的)。因此,选项 2 不满足要求,而选项 3 满足。
我们可能还会倾向于得出结论,选项 1 不满足要求,因为像 thread::spawn
这样的函数要求移动到线程中的所有数据只能包含具有 'static
生命周期 的引用。但是,这并不能排除选项 1,原因有二
Config
可以存储为全局静态变量(例如,使用OnceLock
),因此可以构造&'static Config
引用。- 并非所有并发机制都要求
'static
生命周期,例如thread::scope
。
因此,如上所述的要求仅排除非 Send
类型,我们认为选项 1 和 3 是正确的答案。
现在您尝试下面的问题!每个部分都包含一个专注于单个场景的测验。完成测验,并确保阅读每次测验后的答案上下文。这些问题既是实验性的又是主观的——如果您不同意我们的答案,请通过 bug 按钮 🐞 向我们留下反馈。
除了每个测验之外,我们还提供了指向流行的 Rust crate 的链接,这些 crate 为测验提供了灵感。
参考
灵感来源: Bevy assets, Petgraph node indices, Cargo units
Trait 树
灵感来源: Yew components, Druid widgets
分发
灵感来源: Bevy systems, Diesel queries, Axum handlers