我正在学习rust,偶然发现了一个我不知道如何解决的问题。
我想有一个类型(称为Guess
),这只是一个i32
限制在一定的数字范围内(在下面的例子中在1到100之间)。我目前的解决方案是使用实现new
函数的类型别名来强制执行数字范围。它仍然可以通过正常初始化来创建,而不使用Guess::new()
,因为没有像struct那样的私有字段。
这可以预防吗?也应该防止直接改变值。
代码示例(playground):
type Guess = i32;
trait G {
fn new(val: i32) -> Guess;
}
impl G for Guess {
fn new(val: i32) -> Guess {
if val < 1 || val > 100 {
panic!("Only guesses between 1 and 100 are allowed!");
}
val
}
}
fn main() {
let guess: Guess = 23; // should not be allowed
let guess: Guess = Guess::new(42); // only this should be allowed
println!("Guess this number: {}", guess);
}
type
永远不会给你任何额外的隐私/保证/不变量。type
定义的是严格意义上的别名:"使用该名称作为现有类型的另一个名称"。在您的代码中,Guess
和i32
可以互换使用。
struct
定义一个新类型(而不仅仅是一个现有类型的新名称)。
正如其他人所说,你要做的是使用"newtype"模式,它基本上是将你的类型包装成另一个:
struct Guess(i32);
impl Guess {
fn new(val: i32) -> Self {
assert!(val >= 1 && val <= 100,
"Only guesses between 1 and 100 are allowed!");
Self(val)
}
}
fn main() {
//let guess: Guess = 23; // this is an error now
let guess: Guess = Guess::new(42); // fine
println!("Guess this number: {}", guess.0);
}
主要区别在于用户需要使用guess.0
。
还有其他一些改进:
- 不需要trait来定义方法。
- 使用
Self
避免了类型名的重复。 -
assert!
forif
+panic!
.