《Rust》第9.3章To panic!或者不要惊慌!在为验证创建自定义类型一节中,它们有代码片段
loop {
// --snip--
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
if guess < 1 || guess > 100 {
println!("The secret number will be between 1 and 100.");
continue;
}
match guess.cmp(&secret_number) {
// --snip--
}
,并建议最好创建一个自定义类型,通过
检查值应该在1到100之间的变量。pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess { value }
}
pub fn value(&self) -> i32 {
self.value
}
}
所以这意味着使用Guess结构体的新代码应该是
loop {
// --snip--
let guess: Guess = match guess.trim().parse() {
Ok(num) => Guess::new(num),
Err(_) => continue,
};
match guess.cmp(&secret_number) {
// --snip--
}
然而,我对输入验证逻辑现在去哪里感到困惑
if guess < 1 || guess > 100 {
println!("The secret number will be between 1 and 100.");
continue;
}
构造函数
Guess::new(num)
如果给出了无效的数字,会产生恐慌。那么,Guess::new
不应该返回一个Result
吗?这样我们就可以处理这个问题,让用户知道秘密数在1到100之间?
书上还说
有形参或只返回1到100之间的数字的函数可以在其签名中声明它接受或返回一个Guess而不是一个i32,并且不需要在函数体中做任何额外的检查。
那么这是否意味着我们应该有另一个函数来执行实际的输入验证,然后返回Guess
对象?就像
fn check_input(value: i32) -> Result<Guess, String> {
if value < 1 || value > 100 {
return Err(String::from("The secret number will be between 1 and 100."));
}
Ok(Guess::new(value))
}
// ...
loop {
// --snip--
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
let guess: Guess = match check_input(guess) {
Ok(result) => result,
Err(msg) => {
println!("{}", msg);
continue;
},
};
}
// ...
如果Guess::new
返回一个Result
,那么我们不需要创建check_input
函数。而且我们不会重复验证逻辑(我们现在在Guess构造函数和check_input函数中都有输入验证)。后一种方法的优点是什么?还是我误解了这本书的意思?
如果您对构造函数中的无效值感到恐慌,您应该在调用它之前验证输入:
loop {
// --snip--
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
if guess < 1 || guess > 100 {
println!("The secret number will be between 1 and 100.");
continue;
}
let guess = Guess::new(guess);
match guess.cmp(&secret_number) {
// --snip--
}
在这一点上,由于您没有对Guess
对象做太多操作,我认为它没有价值。
您也可以选择在构造函数中返回Result
(或Option
)。在这种情况下,这将是我的首选选项,因为这避免了双重验证。另一个常见的做法是有一个恐慌的new()
构造函数和一个非恐慌的try_new()
构造函数,以防两者都需要。
那么这是否意味着我们应该有另一个函数来执行实际的输入验证,然后它返回
Guess
对象?
我不认为;你当然可以这样做,但我认为这本书所说的是,如果你有一个函数是已知只返回1到100之间的数字(它也应该返回一些猜测,否则使用Guess
没有意义)它可以返回一个Guess
。