防止直接初始化类型别名

  • 本文关键字:类型 别名 初始化 rust
  • 更新时间 :
  • 英文 :


我正在学习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定义的是严格意义上的别名:"使用该名称作为现有类型的另一个名称"。在您的代码中,Guessi32可以互换使用。

为了实现你想要的范围限制,必须使用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! for if + panic! .

最新更新