Aar*_*468 4 validation rust modular-arithmetic
在Rust中,我需要一个数字类型,其属性具有大约为0的域.如果数字n是有效值,则数字-n也必须有效.如何在初始化和算术期间确保类型安全?如何在类型上实现模块化和饱和算法?
最简单的问题示例是:
type MyNumber = i8; // Bound to domain (-100, 100)
fn main() {
let a = MyNumber(128); // Doesn't panic when 128 > 100
}
Run Code Online (Sandbox Code Playgroud)
需要考虑一些因素,我尝试了不同的解决方案.我将在下面的示例中避免泛型编程:
基于类型关闭枚举确保仅有效值是可能的值.这变得非常快:
enum MyNumber {
One,
Two,
...
}
impl MyNumber {
fn convert(i8) -> MyNumber {
match {
1 => MyNumber::One,
2 => MyNumber::Two,
...
}
}
}
Run Code Online (Sandbox Code Playgroud)在设置字段之前公开一种检查参数的方法,即教科书相关功能.这不会阻止使用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);
}
Run Code Online (Sandbox Code Playgroud)以上解决方案都不是非常优雅 - 在某种程度上这是因为它们是原型实现.必须有一种更简洁的方法来限制数字类型的域!
类型和特征的组合将检查边界,将它们用于模块化/饱和算术,并轻松转换为数字原语?
编辑:这个问题被标记为2014年以来一个更老的问题的副本.我不相信这些问题是相同的,因为Rust是预备alpha版本,并且版本1.0带来了对语言的重大改进.差异比Python 2和3之间的差异更大.
在设置字段之前公开一种检查参数的方法,即教科书相关功能.这不会阻止使用struct构造函数进行赋值.
如果该字段是私有的话.
在Rust中,同一模块或子模块中的函数可以看到私有项......但是如果将类型放入其自己的模块中,则私有字段不可从外部获取:
mod mynumber {
// The struct is public, but the fields are not.
// Note I've used a tuple struct, since this is a shallow
// wrapper around the underlying type.
// Implementing Copy since it should be freely copied,
// Clone as required by Copy, and Debug for convenience.
#[derive(Clone,Copy,Debug)]
pub struct MyNumber(i8);
Run Code Online (Sandbox Code Playgroud)
这是一个简单impl的饱和添加,利用i8内置的saturating_add避免包装,以便简单的夹紧工作.可以使用pub fn new函数构造类型,该函数现在返回,Option<MyNumber>因为它可能会失败.
impl MyNumber {
fn is_in_range(val: i8) -> bool {
val >= -100 && val <= 100
}
fn clamp(val: i8) -> i8 {
if val < -100 {
return -100;
}
if val > 100 {
return 100;
}
// Otherwise return val itself
val
}
pub fn new(val: i8) -> Option<MyNumber> {
if MyNumber::is_in_range(val) {
Some(MyNumber(val))
} else {
None
}
}
pub fn add(&self, other: MyNumber) -> MyNumber {
MyNumber(MyNumber::clamp(self.0.saturating_add(other.0)))
}
}
}
Run Code Online (Sandbox Code Playgroud)
其他模块可以是use这种类型:
use mynumber::MyNumber;
Run Code Online (Sandbox Code Playgroud)
一些例子使用:
fn main() {
let a1 = MyNumber::new(80).unwrap();
let a2 = MyNumber::new(70).unwrap();
println!("Sum: {:?}", a1.add(a2));
// let bad = MyNumber(123); // won't compile; accessing private field
let bad_runtime = MyNumber::new(123).unwrap(); // panics
}
Run Code Online (Sandbox Code Playgroud)
在一个更完整的实现中,我可能会实现std::ops::Add等等,这样我就可以使用a1 + a2而不是调用命名方法.