This commit is contained in:
Xingyu Wang 2020-01-21 22:48:02 +08:00
parent 2bc97881fa
commit 0ef0271f2d

View File

@ -185,92 +185,89 @@ fn enable_register(&mut reg) {
在上面的代码中,预期结果是什么?好吧,代码会将启用位设置为 0因为 `101 = 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&lt;U1,U1, U0&gt;;
type RegInterrupt = Field&lt;U1, U2, U1&gt;;
type RegKind = Field&lt;U3, op!(U7 &lt;&lt; U2), U2&gt;;
type RegEnabled = Field<U1,U1, U0>;
type RegInterrupt = Field<U1, U2, U1>;
type RegKind = Field<U3, op!(U7 << U2), U2>;
impl&lt;Width: Unsigned, Mask: Unsigned, Offset: Unsigned&gt; Field&lt;Width, Mask, Offset&gt; {
    fn new(val: u8) -&gt; Option&lt;Self&gt; {
        if val &lt;= (1 &lt;&lt; Width::U8) - 1 {
            Some(Field {
                value: (val &lt;&lt; Offset::U8) &amp; 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 dont 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&lt;Width: Unsigned, Mask: Unsigned, Offset: Unsigned&gt; {
    value: u8,
    _mask: PhantomData&lt;Mask&gt;,
    _offset: PhantomData&lt;Offset&gt;,
    _width: PhantomData&lt;Width&gt;,
struct Field<Width: Unsigned, Mask: Unsigned, Offset: Unsigned> {
value: u8,
_mask: PhantomData<Mask>,
_offset: PhantomData<Offset>,
_width: PhantomData<Width>,
}
type RegEnabled = Field&lt;U1, U1, U0&gt;;
type RegInterrupt = Field&lt;U1, U2, U1&gt;;
type RegKind = Field&lt;U3, op!(U7 &lt;&lt; U2), U2&gt;;
type RegEnabled = Field<U1, U1, U0>;
type RegInterrupt = Field<U1, U2, U1>;
type RegKind = Field<U3, op!(U7 << U2), U2>;
impl&lt;Width: Unsigned, Mask: Unsigned, Offset: Unsigned&gt; Field&lt;Width, Mask, Offset&gt; {
    const fn new_checked&lt;V: Unsigned&gt;() -&gt; Self
    where
        V: IsLessOrEqual&lt;op!((U1 &lt;&lt; Width) - U1), Output = True&gt;,
    {
        Field {
            value: (V::U8 &lt;&lt; Offset::U8) &amp; 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(&amp;mut reg) {
    reg.update(RegEnabled::new_checked::&lt;U10&gt;());
fn enable_register(&mut reg) {
reg.update(RegEnabled::new_checked::<U10>());
}
12 |     reg.update(RegEnabled::new_checked::&lt;U10&gt;());
   |                           ^^^^^^^^^^^^^^^^ 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