mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-02-22 00:40:10 +08:00
PART 3
This commit is contained in:
parent
231f508597
commit
2bc97881fa
@ -126,14 +126,13 @@ fn enable_register(&mut reg) {
|
||||
|
||||
使用 Rust,你可以使用数据结构来表示字段,将它们附加到特定的寄存器,并在与硬件交互时提供简洁明了的人机工程学。这个例子使用了 Rust 提供的最基本的功能。无论如何,添加的结构都会减轻上述 C 示例中的某些密度。现在,字段是个已命名的事物,而不是从模糊的按位运算符派生而来的数字,并且寄存器是具有状态的类型 —— 这在硬件上多了一层抽象。
|
||||
|
||||
### A Rust implementation for ease of use
|
||||
### 一个易用的 Rust 实现
|
||||
|
||||
The first rewrite in Rust is nice, but it's not ideal. You have to remember to bring the mask and offset, and you're calculating them ad hoc, by hand, which is error-prone. Humans aren't great at precise and repetitive tasks—we tend to get tired or lose focus, and this leads to mistakes. Transcribing the masks and offsets by hand, one register at a time, will almost certainly end badly. This is the kind of task best left to a machine.
|
||||
用 Rust 重写的第一个版本很好,但是并不理想。你必须记住要带上掩码和偏移量,并且要手工进行临时计算,这容易出错。人类不擅长精确且重复的任务 —— 我们往往会感到疲劳或失去专注力,这会导致错误。一次一个寄存器地手动记录掩码和偏移量几乎可以肯定会很糟糕。这是最好留给机器的任务。
|
||||
|
||||
Second, thinking more structurally: What if there were a way to have the field's type carry the mask and offset information? What if you could catch mistakes in your implementation for how you access and interact with hardware registers at compile time instead of discovering them at runtime? Perhaps you can lean on one of the strategies commonly used to suss out issues at compile time, like types.
|
||||
|
||||
You can modify the earlier example by using [`typenum`][6], a library that provides numbers and arithmetic at the type level. Here, you'll parameterize the `Field` type with its mask and offset, making it available for any instance of `Field` without having to include it at the call site:
|
||||
其次,从结构上进行思考:如果有一种方法可以让字段的类型携带掩码和偏移信息呢?如果你要在访问硬件寄存器并与之交互的实现过程中就能发现错误,而不是在运行时才发现,该怎么办?也许你可以依靠一种通常用于在编译时解决问题的策略,例如类型。
|
||||
|
||||
你可以使用 [typenum][6] 来修改前面的示例,该库在类型级别提供数字和算术。在这里,你将使用掩码和偏移量对 `Field` 类型进行参数化,使其可用于任何 `Field` 实例,而不必在调用站点中将其包括在内:
|
||||
|
||||
```
|
||||
#[macro_use]
|
||||
@ -144,49 +143,47 @@ use core::marker::PhantomData;
|
||||
use typenum::*;
|
||||
|
||||
// Now we'll add Mask and Offset to Field's type
|
||||
struct Field<Mask: Unsigned, Offset: Unsigned> {
|
||||
value: u8,
|
||||
_mask: PhantomData<Mask>,
|
||||
_offset: PhantomData<Offset>,
|
||||
struct Field<Mask: Unsigned, Offset: Unsigned> {
|
||||
value: u8,
|
||||
_mask: PhantomData<Mask>,
|
||||
_offset: PhantomData<Offset>,
|
||||
}
|
||||
|
||||
// We can use type aliases to give meaningful names to
|
||||
// our fields (and not have to remember their offsets and masks).
|
||||
type RegEnabled = Field<U1, U0>;
|
||||
type RegInterrupt = Field<U2, U1>;
|
||||
type RegKind = Field<op!(U7 << U2), U2>;
|
||||
type RegEnabled = Field<U1, U0>;
|
||||
type RegInterrupt = Field<U2, U1>;
|
||||
type RegKind = Field<op!(U7 << U2), U2>;
|
||||
```
|
||||
|
||||
Now, when revisiting `Field`'s constructor, you can elide the mask and offset parameters because the type contains that information:
|
||||
|
||||
现在,当重新访问 `Field` 的构造函数时,你可以忽略掩码和偏移量参数,因为类型中包含该信息:
|
||||
|
||||
```
|
||||
impl<Mask: Unsigned, Offset: Unsigned> Field<Mask, Offset> {
|
||||
fn new(val: u8) -> Self {
|
||||
Field {
|
||||
value: (val << Offset::U8) & Mask::U8,
|
||||
_mask: PhantomData,
|
||||
_offset: PhantomData,
|
||||
}
|
||||
}
|
||||
impl<Mask: Unsigned, Offset: Unsigned> Field<Mask, Offset> {
|
||||
fn new(val: u8) -> Self {
|
||||
Field {
|
||||
value: (val << Offset::U8) & Mask::U8,
|
||||
_mask: PhantomData,
|
||||
_offset: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And to enable our register...
|
||||
fn enable_register(&mut reg) {
|
||||
reg.update(RegEnabled::new(1));
|
||||
fn enable_register(&mut reg) {
|
||||
reg.update(RegEnabled::new(1));
|
||||
}
|
||||
```
|
||||
|
||||
It looks pretty good, but… what happens when you make a mistake regarding whether a given value will _fit_ into a field? Consider a simple typo where you put `10` instead of `1`:
|
||||
|
||||
看起来不错,但是……如果你对给定的值是否*适合*某个字段犯了错误,会发生什么?考虑一个简单的输入错误,你在其中放置了 `10` 而不是 `1`:
|
||||
|
||||
```
|
||||
fn enable_register(&mut reg) {
|
||||
fn enable_register(&mut reg) {
|
||||
reg.update(RegEnabled::new(10));
|
||||
}
|
||||
```
|
||||
|
||||
In the code above, what is the expected outcome? Well, the code will set that enabled bit to 0 because `10 & 1 = 0`. That's unfortunate; it would be nice to know whether a value you're trying to write into a field will fit into the field before attempting a write. As a matter of fact, I'd consider lopping off the high bits of an errant field value _undefined behavior_ (gasps).
|
||||
在上面的代码中,预期结果是什么?好吧,代码会将启用位设置为 0,因为 `10&1 = 0`。那真不幸;最好在尝试写入之前知道你要写入字段的值是否适合该字段。事实上,我会考虑放弃错误字段值的高位*未定义行为*(喘气)。
|
||||
|
||||
### Using Rust with safety in mind
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user