我们应该使用u32/ i32或更低的变体(u8/ i8,u16/ i16)来处理有限范围的数字,如"月中的天数",范围从1-30或"主题的得分",范围从0到100?或者为什么我们不应该?
较低的变体(即内存效率)是否有任何优化或好处?
正确性应在性能和正确性明智的(像1-100范围)优先,所有的解决方案(u8,u32,...)也同样糟糕.最好的解决方案是创建一种新类型,以便从强类型中获益.
我的其余部分试图证明这种说法是正确的,并讨论了创建新类型的不同方法.
让我们来看看"主题得分"的例子:唯一的合法值是0-100.我认为正确性,使用u8和u32同样糟糕:在这两种情况下,您的变量都可以保存在语义上下文中不合法的值; 那很糟!
并且认为u8更好,因为有更少的非法价值观,就像争辩说摔跤熊比走过纽约更好,因为你只有一种可能性死亡(熊受攻击失血)而不是许多可能性在纽约的死亡(车祸,刀袭,溺水......)
所以我们想要的是一种保证只保留合法价值的类型.我们想要创建一个完全符合这一要求的新类型.但是,有多种方法可以继续; 每个都有不同的优点和缺点.
struct ScoreOfSubject(pub u8);
Run Code Online (Sandbox Code Playgroud)
优点:至少API更容易理解,因为参数已经由类型解释.更容易理解的是:
add_record("peter", 75, 47) 要么 add_record("peter", StudentId(75), ScoreOfSubject(47))?我会说后者;-)
缺点:我们实际上没有进行任何范围检查,仍然可能发生非法值; 坏!.
struct ScoreOfSubject(pub u8);
impl ScoreOfSubject {
pub fn new(value: u8) -> Self {
assert!(value <= 100);
ScoreOfSubject(value)
}
pub fn get(&self) -> u8 { self.0 }
}
Run Code Online (Sandbox Code Playgroud)
优势:我们用很少的代码强制执行合法的价值观,是的:)
缺点:使用该类型可能很烦人.几乎每个操作都需要程序员打包和解包值.
(代码会impl Add<_>,impl Display等等)
优点:程序员可以直接使用该类型并对其执行所有有用的操作 - 通过范围检查!这是非常理想的.
请看看Matthieu M.的评论:
[...]通常将分数相乘或除以它们不会产生分数!强类型不仅强制执行有效值,还强制执行有效操作,因此您实际上不会将两个分数分开以获得另一个分数.
我认为这是一个非常重要的一点,我以前没有说清楚.强类型可以防止程序员对值执行非法操作(没有任何意义的操作).一个很好的例子是cgmath用于区分点向量和方向向量的箱子,因为它们都支持不同的操作.您可以在此处找到其他说明.
缺点:很多代码:(
幸运的是,Rust宏/编译器插件系统可以减少这个缺点.有像newtype_derive或者bounded_integer那样的箱子为你做这种代码生成(免责声明:我从未与他们合作过).
但现在你说:"你不能认真吗?我应该花时间写新类型吗?"
不一定,但如果你正在研究生产代码(==至少有些重要),那么我的答案是:是的,你应该.
| 归档时间: |
|
| 查看次数: |
523 次 |
| 最近记录: |