我有一个函数,它的参数类型是u16
。有没有一种优雅的方式来定义一个自定义数据类型,它的行为与u16
完全相同,但只有0到100之间的值?
#[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);
assert_eq!(
std::mem::size_of::<Age>(),
std::mem::size_of::<u16>()
);
}
TryFrom
(从到您的类型)或From
(从您的类型到)以更好地与通用代码集成。很不幸,在 std crate 中没有这样的东西。
但是,你可以使用夜版泛型常量以优化的方式自己实现它,这项功能计划在 Rust 1.51 版本中稳定。例如:
// 1.51.0-nightly (2020-12-30)
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
这样的箱子,它允许你使用宏在运行时生成一个有界整数。
有了每夜更新功能 generic_const_exprs
,就可以在编译时验证:
#![feature(generic_const_exprs)]
struct If<const COND: bool>;
trait True {}
impl True for If<true> {}
const fn in_bounds(n: usize, low: usize, high: usize) -> bool {
n > low && n < high
}
struct BoundedInteger<const LOW: usize, const HIGH: usize>(usize);
impl<const LOW: usize, const HIGH: usize> BoundedInteger<LOW, HIGH>
where
If<{ LOW < HIGH }>: True,
{
fn new<const N: usize>() -> Self
where
If<{ in_bounds(N, LOW, HIGH) }>: True,
{
Self(N)
}
}
错误信息不是最好的,但它可以工作!
fn main() {
let a = BoundedInteger::<1, 10>::new::<5>();
let b = BoundedInteger::<10, 1>::new::<5>(); // ERROR: doesn't satisfy `If<{ LOW < HIGH }>: True`
let c = BoundedInteger::<2, 5>::new::<6>(); // ERROR: expected `false`, found `true`
}
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()
);
}
Age
放在一个模块中,并不要将字段公开。 - Shepmaster