rust - `u32`/`i32` 即使在有限范围的情况下也被建议吗?

标签 rust

我们应该使用 u32/i32 还是它的低版本 (u8/i8, u16 /i16) 在处理范围有限的数字时,例如范围从 1-30 的“月中的天数”或范围从 0 到 100 的“主题分数”?或者为什么我们不应该这样做?

较低版本(即内存效率)是否有任何优化或优势?

最佳答案

总结

正确性应优先于性能和正确性(对于 1-100 之类的范围),所有解决方案( u8u32 ……)都同样糟糕。最好的解决方案是创建一个新类型以受益于强类型

我的其余回答试图证明这一说法并讨论创建新类型的不同方法。

更多解释

让我们看一下“主题得分”示例:唯一的合法值是 0–100。我认为在正确性方面,使用 u8u32 同样糟糕:在这两种情况下,您的变量可以保存在您的语义上下文中合法的值;那很糟!

争论 u8 更好,因为非法值更少,就像争论与熊搏斗比步行穿过纽约更好,因为你只有一种死亡的可能性(被熊攻击失血)而不是到纽约死亡的多种可能性(车祸、持刀袭击、溺水……)。

所以我们想要的是一种保证只持有合法值的类型。我们想创建一个新的类型来做这件事。但是,有多种方法可以继续;每个都有不同的优点和缺点。


(A) 公开内在值(value)

struct ScoreOfSubject(pub u8);

优点:至少API更容易理解,因为参数已经用类型解释了。什么更容易理解:

  • add_record("peter", 75, 47)
  • add_record("peter", StudentId(75), ScoreOfSubject(47)) ?

我会说后者 ;-)

缺点:我们实际上没有做任何范围检查,非法值仍然会出现;不好!<​​/p>


(B) 将内部值设为私有(private)并提供范围检查构造函数

struct ScoreOfSubject(pub u8);

impl ScoreOfSubject {
    pub fn new(value: u8) -> Self {
        assert!(value <= 100);
        ScoreOfSubject(value)
    }
    pub fn get(&self) -> u8 { self.0 }
}

优点:我们用很少的代码执行合法值,是的:)

缺点:使用类型可能很烦人。几乎每个操作都需要程序员打包和解包值。


(C) 添加一堆实现(除了(B))

(代码为 impl Add<_>impl Display 等)

优点:程序员可以直接使用该类型并对其执行所有有用的操作——带有范围检查!这是非常理想的。

请看一下 Matthieu M. 的评论:

[...] generally multiplying scores together, or dividing them, does not produce a score! Strong typing not only enforces valid values, it also enforces valid operations, so that you don't actually divide two scores together to get another score.

我觉得这是我之前没有说清楚的一个很重要的点。强类型防止程序员对值执行非法操作(没有任何意义的操作)。一个很好的例子是 crate cgmath ,它区分点和方向向量,因为它们都支持对它们的不同操作。你可以找到额外的解释 here

缺点:代码很多:(

幸运的是,Rust 宏/编译器插件系统可以减少这个缺点。有像 newtype_derive bounded_integer 这样的 crate 可以为您生成这种代码(免责声明:我从未与他们合作过)。


但现在你说:“你不是认真的吗?我应该花时间写新类型吗?”。

不一定,但如果您正在处理生产代码(== 至少有些重要),那么我的回答是:是的,您应该

关于rust - `u32`/`i32` 即使在有限范围的情况下也被建议吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39939386/

相关文章:

rust - 具有父类(super class)型生命周期的引用不能用于子类型生命周期

rust - 为什么在 `tail` 循环后仍然借用了 `while let Some() = xxx`?

rust - 如何包含来自同一项目的另一个文件的模块?

rust - rust 主fn读取目录结果错误无法编译

sorting - 如何在不将迭代器全部放入向量中的情况下对迭代器进行排序?

generics - 为什么特征内的泛型方法需要调整特征对象的大小?

rust - 创建 cargo 后,我在运行这些 cargo 时遇到了一些问题

rust - 为什么 `&str` 实现函数(例如 `String` )的 `String::trim` 结果不像静态字符串文字?

rust - 如何将 Cargo.toml 文件中特定版本的依赖项列入黑名单?

rust - 我们如何在 sqlx rust 中定义 jsonb 和 UUID 字段?