I'm going through the rust book and was doing an exercise converting Fahrenheit to Celsius. I came up with two ways of error handling the input. A loop, similar to how the guessing game in Ch. 2 of the book did it, and a recursive function. I have zero expectation of the recursive function being better, but I wanted to experiment.
In doing so I've come across a confusing situation. In my loop code I am able to break from the loop returning the number. But I can't do the same with the function. The error is expecting f32 as the return type but is getting the unit () instead.
I'm not sure why
Ok(num) => break num,
returns the correct f32 value but Ok(num) => return num,
returns ()
Loop based error handling code
use std::io;
fn main() {
println!("Please input a temperature in Fehrenheit: ");
let inputed_fahrenheit: f64 = loop {
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read lines");
match input.trim().parse(){
Ok(num) => break num,
Err(_) => {
println!("Please input valid 32-bit floating point number.");
continue;
}
};
};
let converted_temperature: f64 = convert_f_temperature_to_c(inputed_fahrenheit);
println!("{converted_temperature}");
}
fn convert_f_temperature_to_c(f_temp: f64) -> f64{
((f_temp - 32.0) * 5.0) / 9.0
}
Recursive function based error handling
use std::io;
fn main() {
println!("Please input a temperature in Fehrenheit: ");
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read lines");
let inputed_fahrenheit: f64 = match input.trim().parse(){
Ok(num) => num,
Err(_) => invalid_input()
};
let converted_temperature: f64 = convert_f_temperature_to_c(inputed_fahrenheit);
println!("{converted_temperature}");
}
fn invalid_input() -> f64{
println!("Please input valid 32-bit floating point number.");
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read lines");
match input.trim().parse(){
Ok(num) => return num,
Err(_) => invalid_input()
};
}
fn convert_f_temperature_to_c(f_temp: f64) -> f64{
((f_temp - 32.0) * 5.0) / 9.0
}
Compile error for the recursive function code
error[E0308]: mismatched types
--> src\main.rs:22:23
|
22 | fn invalid_input() -> f64{
| ------------- ^^^ expected `f64`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
...
33 | };
| - help: remove this semicolon to return this value
For more information about this error, try `rustc --explain E0308`.
error: could not compile `convert` (bin "convert") due to 1 previous error
As a person, you can see that this function either returns f64
or recurses until it does, and the recursive call clearly ends up in the same logic. So, you conclude that this function must eventually return a f64
.
However, the Rust compiler does not perform whole-program analysis or recursion convergence proofs, so it doesn't infer that the recursive call guarantees a return. Instead, it checks each branch independently. In this case, the Err(_)
arm evaluates to a f64
but that value isn't used — it's not returned, assigned, or even known. As a result, that branch implicitly evaluates to ()
, and the function as a whole appears to the compiler to not always return a f64
.
Also, recursion like this can lead to a stack overflow in principle (if you keep entering invalid input), so the function isn't even guaranteed to return a f64
.
For the loop, it accepts that the only normal way out of the loop is with break num
and so the type matches the defined type, which explains why it does accept the loop implementation, but doesn't accept the recursive implementation.
As others have pointed out in the comments, removing the semicolon would at least return the value of the match
-expression. The error you shared seems to suggest that this is what you are asking about.