假设我有一个带有u16
类型参数的函数。是否有一种优雅的方式来定义一个行为与u16
完全相同但只有0到100之间的值的自定义数据类型?
答案 0 :(得分:8)
据我所知,这需要dependent types,Rust没有。这不需要依赖类型(请参阅注释)但Rust仍然没有所需的支持
作为一种解决方法,您可以创建一个自己验证的新类型:
#[derive(Debug)]
struct Age(u16);
impl Age {
fn new(age: u16) -> Option<Age> {
if age <= 100 {
Some(Age(age))
} else {
None
}
}
}
fn main() {
let age1 = Age::new(30);
let age2 = Age::new(500);
println!("{:?}, {:?}", age1, age2);
println!("{}, {}", std::mem::size_of::<Age>(), std::mem::size_of::<u16>());
}
当然,它不像<{1}}那样完全,但你也不想要它!例如,u16
可以超过100 ...你必须推断是否有必要对你的新类型进行加/减/乘/除等。
需要注意的一点是,这个newtype占用的空间与u16
相同 - 在编译代码时,包装类型会被有效擦除。类型检查器确保在该点之前所有内容都是网格。
答案 1 :(得分:3)
不幸的是,std板条箱中没有这样的东西。
但是,您可以使用夜间通用const以优化的方式自己进行操作。示例:
#![feature(const_generics)]
pub struct BoundedI32<const LOW: i32, const HIGH: i32>(i32);
impl<const LOW: i32, const HIGH: i32> BoundedI32<{LOW}, {HIGH}> {
pub const LOW: i32 = LOW;
pub const HIGH: i32 = HIGH;
pub fn new(n: i32) -> Self {
BoundedI32(n.min(Self::HIGH).max(Self::LOW))
}
pub fn fallible_new(n: i32) -> Result<Self, &'static str> {
match n {
n if n < Self::LOW => Err("Value too low"),
n if n > Self::HIGH => Err("Value too high"),
n => Ok(BoundedI32(n)),
}
}
pub fn set(&mut self, n: i32) {
*self = BoundedI32(n.min(Self::HIGH).max(Self::LOW))
}
}
impl<const LOW: i32, const HIGH: i32> std::ops::Deref for BoundedI32<{LOW}, {HIGH}> {
type Target = i32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let dice = BoundedI32::<1, 6>::fallible_new(0);
assert!(dice.is_err());
let mut dice = BoundedI32::<1, 6>::new(0);
assert_eq!(*dice, 1);
dice.set(123);
assert_eq!(*dice, 6);
}
然后您可以实现数学等。
如果要在运行时选择边界,则不需要此功能,而只需要执行以下操作即可:
pub struct BoundedI32 {
n: i32,
low: i32,
high: i32,
}
您还可以使用像bounded-integer
这样的板条箱,该板条箱可以通过宏即时生成有界整数。
答案 2 :(得分:0)
据我所知,不完全是。但是您可以使用特征来接近。例如,其中吨位是一个无符号的 8 位整数,预计为 20-100 和 5 的倍数:
pub trait Validator{
fn isvalid(&self) -> bool;
}
pub struct TotalRobotTonnage{
pub tonnage: u8,
}
impl Validator for TotalRobotTonnage{
//is in range 20-100 and a multiple of 5
fn isvalid(&self) -> bool{
if self.tonnage < 20 || self.tonnage > 100 || self.tonnage % 5 != 0{
false
}else{
true
}
}
}
fn main() {
let validtonnage = TotalRobotTonnage{tonnage: 100};
let invalidtonnage_outofrange = TotalRobotTonnage{tonnage: 10};
let invalidtonnage_notmultipleof5 = TotalRobotTonnage{tonnage: 21};
println!("value {} [{}] value {} [{}] value {} [{}]",
validtonnage.tonnage,
validtonnage.isvalid(),
invalidtonnage_outofrange.tonnage,
invalidtonnage_outofrange.isvalid(),
invalidtonnage_notmultipleof5.tonnage,
invalidtonnage_notmultipleof5.isvalid()
);
}