【发布时间】:2017-01-08 06:32:34
【问题描述】:
在 Rust 中,我需要一个数字类型,它的属性是域围绕 0 对称。如果数字 n 是有效值,那么数字 -n em> 也必须有效。在初始化和算术期间如何确保类型安全?在类型上实现模数和饱和算法的最佳方式是什么?
问题最简单的例子是:
type MyNumber = i8; // Bound to domain (-100, 100)
fn main() {
let a = MyNumber(128); // Doesn't panic when 128 > 100
}
需要考虑一些因素,我尝试了不同的解决方案。对于下面的示例,我将避免使用泛型编程:
-
基于枚举类型确保只有有效值是可能的值。这很快就会变得一团糟:
enum MyNumber { One, Two, ... } impl MyNumber { fn convert(i8) -> MyNumber { match { 1 => MyNumber::One, 2 => MyNumber::Two, ... } } } 公开一个在设置字段之前检查参数的方法,教科书associated function。这并不妨碍使用 struct 构造函数进行赋值。
-
每当操作发生时验证操作数(并强制纠正它们)。这看起来很合理,但需要每个方法重复验证代码。
extern crate num; use num::Bounded; use std::cmp; struct MyNumber { val: i8, } impl Bounded for MyNumber { fn max_value() -> Self { MyNumber { val: 65 } } fn min_value() -> Self { MyNumber { val: -50 } } } impl MyNumber { fn clamp(&mut self) { self.val = cmp::min(MyNumber::max_value().val, cmp::max(MyNumber::min_value().val, self.val)) } fn add(&mut self, mut addend: Self) { self.clamp(); addend.clamp(); //TODO: wrap or saturate result self.val = self.val + addend.val } } fn main() { let mut a = MyNumber { val: i8::max_value() }; let b = MyNumber { val: i8::min_value() }; a.add(b); println!("{} + {} = {}", MyNumber::max_value().val, MyNumber::min_value().val, a.val); }
上述解决方案都不是非常优雅的 - 在某种程度上这是因为它们是原型实现。必须有一种更简洁的方法来限制数字类型的域!
类型和特征的哪种组合可以检查边界,将它们用于模/饱和算术,并轻松转换为数字基元?
编辑:这个问题已被标记为来自2014 的一个更老的问题的重复。我不相信这些问题是相同的,因为 Rust 是 pre alpha,并且版本 1.0 带来了对该语言的重大改进。与 Python 2 和 3 相比,差距更大。
【问题讨论】:
-
即使 如果 更改或添加了足够大的内容到 Rust,将这些答案添加到现有问题仍然是合适的。否则,Stack Overflow 上的每一个问题都需要每隔几个月重新提出一次,以防周围环境发生了变化。您在此处收到的答案与先前存在的答案相呼应(直至构造函数和特征实现),因此我没有看到它们不重复的原因。如果您认为一个较老的问题需要更多关注,还有赏金途径。
-
@Shepmaster 感谢您的澄清。这是对此事的公平看法。
-
别担心!更广泛的社区可能仍然不同意我并投票重新开放;我只是在努力保持整洁。 ^_^
-
@Aaron3468 没有版本相关的 rust 标记?
-
@MYGz 虽然它们存在并且我认为还有其他因素会减少版本标签的数量(rust-0.8、rust-0.9、rust-0.11),但您的暗示是公平的。目前看来“生锈”是 1.0+,之前的一切都不是很生锈。但这是我第一次观察到一种年轻的语言成为事实上的语言,所以我不知道术语——或者需要区分——已经固化到什么程度。
标签: validation rust modular-arithmetic