【问题标题】:Prevent direct initialization of type alias防止直接初始化类型别名
【发布时间】:2021-01-17 01:42:28
【问题描述】:

我目前正在学习 rust,偶然发现了一个我不知道如何解决的问题。

我想要一个类型(称为Guess),这只是一个限制在某个数字范围内的i32(在下面的示例中,介于 1 和 100 之间)。我当前的解决方案是使用实现new 函数的类型别名来强制数字范围。仍然可以通过正常初始化创建它,而不使用Guess::new(),因为没有像结构中那样的私有字段。

这可以预防吗? 还应防止直接更改值。

代码示例(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);
}

【问题讨论】:

  • 这能回答你的问题吗? Disable default constructor in Rust?
  • @pretzelhammer 遗憾的是没有。这适用于具有私有字段的结构,但这里不是这种情况。
  • 为什么不能将Guess 变成一个带有私有i32 的结构?
  • @pretzelhammer 总是需要访问该字段。如果可以直接访问该值,我会发现它更简单。
  • newtype idiom 是制作Guess 强类型的方法。如果不允许未经检查的构造和未经检查的分配,则必须将其放入某种包装器中,例如 newtype 或 pretzelhammer 建议的结构。

标签: rust


【解决方案1】:

type从不为您提供任何额外的隐私/保证/不变量。 type 定义的严格来说是一个别名:“将此名称用作此现有类型的另一个名称”。在您的代码中,Guessi32 可以互换使用。

为了实现您想要的范围限制,您必须使用struct 定义一个新类型(而不仅仅是现有类型的新名称)。

【讨论】:

    【解决方案2】:

    正如其他人所说,您要做的是使用“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!if + panic!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多