我有一个非常奇怪的疑问。
我正在学习铁锈编程书,在一章中,为了练习,他们建议做一个温度转换器。。。我确实做到了,问题是我不明白为什么有效。
主要的问题出现在我试图复制一段代码时,该代码允许我比较用户引入的值是否为浮点类型。
当我拿了那个代码并试图编译时,我得到了下一个错误。
Compiling enums v0.1.0 (C:UsersrubenProyectosRustthe_rust_programming_languageenums)
error[E0308]: mismatched types
--> srcmain.rs:96:35
|
96 | Err(parse_float_error) => {
| ___________________________________^
97 | | println!("Please input a valid value. _{}_", parse_float_error);
98 | | }
| |_________^ expected `f64`, found `()`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `enums` due to previous error
这是产生错误的代码:
use std::io;
fn user_input() -> String {
let mut user_input: String = String::new();
io::stdin()
.read_line(&mut user_input)
.expect("Failed to read line");
return user_input;
}
fn main() {
let fahrenheit: f64 = match user_input().trim().parse() {
Ok(f64) => f64,
Err(parse_float_error) => {
println!("Please input a valid value. _{}_", parse_float_error);
}
};
}
工作的完整版本的温度转换器可以在这里找到
最有可能的是,必须有更好的方法来构建代码,所以如果你们中的一些人有建议,我会很乐意阅读。
我知道问题出在类型enum
上,问题是我真的不明白enums
是如何工作的。
对我来说,简单地说,当一段数据是enum
中可用数据的一种或另一种时,就会使用enum
,我得到了一个帮助我理解它的例子:枚举。问题是我真的,真的不明白,这本书也无济于事。
我知道这是一个非常普遍的问题,但如果有人能追随北方,那会有很大帮助。
谢谢。
对我来说,用一种简单的方式,当一段数据是枚举中可用数据的一种或另一种时,使用枚举
大多数情况下,枚举可以有一个或多个变体,并且每个变体都可以(但不必)有关联的数据。每个变体的数据类型可以相同也可以不同。
result
枚举特别有两种变体。Ok
变体表示操作成功。Err
变体表示存在错误。result
是一个泛型类型,它有两个类型参数,T
表示预期结果的类型,E
表示错误的类型。
在这一点上值得注意的是,rust中推断类型的方式与C、C++和许多其他流行语言非常不同。在C/C++中确定表达式类型的过程从表达式的最内部向外流动。每个函数调用或运算符都是根据其参数的类型来选择的,这决定了其结果的类型,而结果又成为下一步的参数。在Rust中,类型推理可以双向运行。表达式中的类型可以而且通常是根据您对结果所做的操作推断出来的。
匹配表达式允许您分解枚举,并根据您的变体采取不同的操作。
考虑到所有这些,让我们分解您的代码。
let fahrenheit: f64 =
我们声明了一个名为fahrenheit
的变量,并明确地告诉编译器它的类型。当编译器试图解释表达式时,此类型将驱动类型检查和推理。
match
我们开始我们的比赛表情。
user_input()
我们调用一个函数,所说的函数在任何方面都不是泛型的,所以这为编译器提供了另一种类型String
。
.trim()
我们在String上调用trim方法。同样,这在任何方面都不是通用的,所以我们再次有一个固定类型&str
。
.parse()
现在,事情变得更有趣了;解析";是一个通用函数。它返回一个result
,但result
的通用参数不是固定的,它们必须根据用户对结果的处理以及parse
签名中的限制来推断。
Ok(f64) => f64,
如果结果是Ok
,我们将打开值并返回它。这里使用f64
有点不幸,它看起来像一个类型名,但实际上更像一个变量名。如果您编写了Ok(v) => v,
,那么您的代码将表现出相同的行为,并且更加地道
在任何情况下,表达式的这一部分都可以让类型推理完成它的工作。您已经在Ok
变体上进行了匹配,并将结果视为f64。所以T
必须是f64
。将其与parse
的签名和FromStr
对f64
的实现相结合意味着E
必须是ParseFloatError
。
Err(parse_float_error) => {
println!("Please input a valid value. _{}_", parse_float_error);
}
这就是我们错的地方。我们已经打印了一条错误消息,但实际上我们还没有处理错误!我们仍然有一个表达式需要一个f64,但我们没有!我们需要以一种方式来处理错误,要么给我们一个有效的f64,要么向编译器承诺它不需要它。
有几种方法可以做到这一点。
- 您可以选择一个默认值(如Paul的回答)
0.0f64
- 你可能会恐慌
panic!("foo {}",parse_float_error)
- 您可以调用一个承诺永远不会正常返回的函数,例如
abort()
- 您可以从函数
return
返回 - 如果代码在循环中,则可以使用循环控制结构
break
或continue
- 如果你绝对肯定错误永远不会发生,你可以告诉编译器它们永远不会发生。如果你告诉编译器某些事情永远不会发生,而且无论如何都会发生,那就是未定义的行为!在这种情况下,这绝对是NOT合适的,但我将其包括在内是为了完整性
unsafe { std::hint::unreachable_unchecked() }
你需要从中选择一个并完成。
检查字符串是否表示浮点值的最佳方法是解析它,代码就是这样做的。从本质上分析用户输入充满了错误案例,因此您不可避免地必须执行大量错误处理,因为Rust的设计/本质迫使您处理错误。
代码中唯一真正的问题是编译器出现错误。您的let
。。。match
臂返回不同的类型:f64
和()
(单元结构),如错误消息所示。为了解决这个问题,从Err臂返回一个f64,就像Ok臂一样:
...
Err(parse_float_error) => {
println!("Please input a valid value. _{}_", parse_float_error);
0.0f64
}