mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-10 22:21:11 +08:00
PART 4
This commit is contained in:
parent
2bc97881fa
commit
0ef0271f2d
@ -185,92 +185,89 @@ fn enable_register(&mut reg) {
|
||||
|
||||
在上面的代码中,预期结果是什么?好吧,代码会将启用位设置为 0,因为 `10&1 = 0`。那真不幸;最好在尝试写入之前知道你要写入字段的值是否适合该字段。事实上,我会考虑放弃错误字段值的高位*未定义行为*(喘气)。
|
||||
|
||||
### Using Rust with safety in mind
|
||||
### 出于安全考虑使用 Rust
|
||||
|
||||
How can you check that a field's value fits in its prescribed position in a general way? More type-level numbers!
|
||||
|
||||
You can add a `Width` parameter to `Field` and use it to verify that a given value can fit into the field:
|
||||
如何以一般方式检查字段的值是否适合其规定的位置?需要更多类型级别的数字!
|
||||
|
||||
你可以在 `Field` 中添加 `Width` 参数,并使用它来验证给定的值是否适合该字段:
|
||||
|
||||
```
|
||||
struct Field<Width: Unsigned, Mask: Unsigned, Offset: Unsigned> {
|
||||
value: u8,
|
||||
_mask: PhantomData<Mask>,
|
||||
_offset: PhantomData<Offset>,
|
||||
_width: PhantomData<Width>,
|
||||
struct Field<Width: Unsigned, Mask: Unsigned, Offset: Unsigned> {
|
||||
value: u8,
|
||||
_mask: PhantomData<Mask>,
|
||||
_offset: PhantomData<Offset>,
|
||||
_width: PhantomData<Width>,
|
||||
}
|
||||
|
||||
type RegEnabled = Field<U1,U1, U0>;
|
||||
type RegInterrupt = Field<U1, U2, U1>;
|
||||
type RegKind = Field<U3, op!(U7 << U2), U2>;
|
||||
type RegEnabled = Field<U1,U1, U0>;
|
||||
type RegInterrupt = Field<U1, U2, U1>;
|
||||
type RegKind = Field<U3, op!(U7 << U2), U2>;
|
||||
|
||||
impl<Width: Unsigned, Mask: Unsigned, Offset: Unsigned> Field<Width, Mask, Offset> {
|
||||
fn new(val: u8) -> Option<Self> {
|
||||
if val <= (1 << Width::U8) - 1 {
|
||||
Some(Field {
|
||||
value: (val << Offset::U8) & Mask::U8,
|
||||
_mask: PhantomData,
|
||||
_offset: PhantomData,
|
||||
_width: PhantomData,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl<Width: Unsigned, Mask: Unsigned, Offset: Unsigned> Field<Width, Mask, Offset> {
|
||||
fn new(val: u8) -> Option<Self> {
|
||||
if val <= (1 << Width::U8) - 1 {
|
||||
Some(Field {
|
||||
value: (val << Offset::U8) & Mask::U8,
|
||||
_mask: PhantomData,
|
||||
_offset: PhantomData,
|
||||
_width: PhantomData,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you can construct a `Field` only if the given value fits! Otherwise, you have `None`, which signals that an error has occurred, rather than lopping off the high bits of the value and silently writing an unexpected value.
|
||||
现在,只有给定值适合时,您才能构造一个 `Field`!否则,你将得到 `None` 信号,该信号指示发生了错误,而不是放弃该值的高位并静默写入意外的值。
|
||||
|
||||
Note, though, this will raise an error at runtime. However, we knew the value we wanted to write beforehand, remember? Given that, we can teach the compiler to reject entirely a program which has an invalid field value—we don’t have to wait until we run it!
|
||||
|
||||
This time, you'll add a _trait bound_ (the `where` clause) to a new realization of new, called `new_checked`, that asks the incoming value to be less than or equal to the maximum possible value a field with the given `Width` can hold:
|
||||
但是请注意,这将在运行时引发错误。但是,我们知道我们想事先写入的值,还记得吗?鉴于此,我们可以教编译器完全拒绝具有无效字段值的程序 —— 我们不必等到运行它!
|
||||
|
||||
这次,您将向 `new` 的新实现 `new_checked` 中添加一个特征绑定(`where` 子句),该函数要求输入值小于或等于给定字段用 ``Width` 所控制的最大可能值:
|
||||
|
||||
```
|
||||
struct Field<Width: Unsigned, Mask: Unsigned, Offset: Unsigned> {
|
||||
value: u8,
|
||||
_mask: PhantomData<Mask>,
|
||||
_offset: PhantomData<Offset>,
|
||||
_width: PhantomData<Width>,
|
||||
struct Field<Width: Unsigned, Mask: Unsigned, Offset: Unsigned> {
|
||||
value: u8,
|
||||
_mask: PhantomData<Mask>,
|
||||
_offset: PhantomData<Offset>,
|
||||
_width: PhantomData<Width>,
|
||||
}
|
||||
|
||||
type RegEnabled = Field<U1, U1, U0>;
|
||||
type RegInterrupt = Field<U1, U2, U1>;
|
||||
type RegKind = Field<U3, op!(U7 << U2), U2>;
|
||||
type RegEnabled = Field<U1, U1, U0>;
|
||||
type RegInterrupt = Field<U1, U2, U1>;
|
||||
type RegKind = Field<U3, op!(U7 << U2), U2>;
|
||||
|
||||
impl<Width: Unsigned, Mask: Unsigned, Offset: Unsigned> Field<Width, Mask, Offset> {
|
||||
const fn new_checked<V: Unsigned>() -> Self
|
||||
where
|
||||
V: IsLessOrEqual<op!((U1 << Width) - U1), Output = True>,
|
||||
{
|
||||
Field {
|
||||
value: (V::U8 << Offset::U8) & Mask::U8,
|
||||
_mask: PhantomData,
|
||||
_offset: PhantomData,
|
||||
_width: PhantomData,
|
||||
}
|
||||
}
|
||||
impl<Width: Unsigned, Mask: Unsigned, Offset: Unsigned> Field<Width, Mask, Offset> {
|
||||
const fn new_checked<V: Unsigned>() -> Self
|
||||
where
|
||||
V: IsLessOrEqual<op!((U1 << Width) - U1), Output = True>,
|
||||
{
|
||||
Field {
|
||||
value: (V::U8 << Offset::U8) & Mask::U8,
|
||||
_mask: PhantomData,
|
||||
_offset: PhantomData,
|
||||
_width: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Only numbers for which this property holds has an implementation of this trait, so if you use a number that does not fit, it will fail to compile. Take a look!
|
||||
|
||||
只有拥有此属性的数字才具有此特征的实现,因此,如果使用不适合的数字,它将无法编译。让我们看一看!
|
||||
|
||||
```
|
||||
fn enable_register(&mut reg) {
|
||||
reg.update(RegEnabled::new_checked::<U10>());
|
||||
fn enable_register(&mut reg) {
|
||||
reg.update(RegEnabled::new_checked::<U10>());
|
||||
}
|
||||
12 | reg.update(RegEnabled::new_checked::<U10>());
|
||||
| ^^^^^^^^^^^^^^^^ expected struct `typenum::B0`, found struct `typenum::B1`
|
||||
|
|
||||
= note: expected type `typenum::B0`
|
||||
found type `typenum::B1`
|
||||
12 | reg.update(RegEnabled::new_checked::<U10>());
|
||||
| ^^^^^^^^^^^^^^^^ expected struct `typenum::B0`, found struct `typenum::B1`
|
||||
|
|
||||
= note: expected type `typenum::B0`
|
||||
found type `typenum::B1`
|
||||
```
|
||||
|
||||
`new_checked` will fail to produce a program that has an errant too-high value for a field. Your typo won't blow up at runtime because you could never have gotten an artifact to run.
|
||||
`new_checked` 将无法生成一个程序,因为该字段的值错误地过高。你的输入错误不会在运行时爆炸,因为你永远无法获得一个可以运行的工件。
|
||||
|
||||
You're nearing Peak Rust in terms of how safe you can make memory-mapped hardware interactions. However, what you wrote back in the first example in C was far more succinct than the type parameter salad you ended up with. Is doing such a thing even tractable when you're talking about potentially hundreds or even thousands of registers?
|
||||
就使内存映射的硬件进行交互的安全性而言,你已经接近 Peak Rust。但是,你在 C 的第一个示例中所写的内容比最终得到的一锅粥类型参数更简洁。当你谈论潜在的数百甚至数千个寄存器时,这样做是否容易处理?
|
||||
|
||||
### Just right with Rust: both safe and accessible
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user