Reputation: 241
With the following code, I tried to return &str of temperature of user input, but in vain. Then, I am trying to return f32, but still struggle...
Q1. The reason I am getting the error at the bottom is because the scope of 'let temp = String::new();
' still persists, even I 'shadow' it later by 'let temp = temp.trim().parse::<f32>();
' within the loop?
Q2. How can I rewrite the code so that it returns &str?
fn gettemp() -> f32 {
let temp = String::new();
loop {
println!("What is your temperature?");
io::stdin().read_line(&mut temp).expect("Failed to read the line");
let temp = temp.trim().parse::<f32>();
if !temp.is_ok() {
println!("Not a number!");
} else {
break;
}
}
temp
}
Error:
error[E0308]: mismatched types
--> src/main.rs:70:5
|
49 | fn gettemp() -> f32 {
| --- expected `f32` because of return type
...
70 | temp
| ^^^^ expected f32, found struct `std::string::String`
|
= note: expected type `f32`
found type `std::string::String`
Upvotes: 0
Views: 151
Reputation: 21249
A1 - nope, that's not how shadowing works. Let's look at your code with comments.
fn gettemp() -> f32 {
let temp = String::new(); // Outer
loop {
// There's no inner temp at this point, even in the second
// loop pass, etc.
println!("What is your temperature?");
// Here temp refers to the outer one (outside of the loop)
io::stdin().read_line(&mut temp).expect("Failed to read the line");
// Shadowed temp = let's call it inner temp
let temp = temp.trim().parse::<f32>();
// ^ ^
// | |- Outer temp
// |- New inner temp
// temp refers to inner temp
if !temp.is_ok() {
println!("Not a number!");
} else {
// Inner temp goes out of scope
break;
}
// Inner temp goes out of scope
}
// Here temp refers to outer one (String)
temp
}
A2 - you can't return &str
. @E_net4 posted a link to the answer why. However, you can return String
. You can do something like this nn case you'd like to have a validated String
:
fn gettemp() -> String {
loop {
println!("What is your temperature?");
let mut temp = String::new();
io::stdin()
.read_line(&mut temp)
.expect("Failed to read the line");
let trimmed = temp.trim();
match trimmed.parse::<f32>() {
Ok(_) => return trimmed.to_string(),
Err(_) => println!("Not a number!"),
};
}
}
I see couple of another problems in your code.
let temp = String::new();
Should be let mut temp
, because you'd like to borrow mutable reference later (&mut temp
in the read_line
call).
Another issue is the loop
& read_line
. read_line
appends to the String
. Run this code ...
let mut temp = "foo".to_string();
io::stdin().read_line(&mut temp).unwrap();
println!("->{}<-", temp);
... and enter 10
for example. You'll see following output ...
->foo10
<-
... which is not what you want. I'd rewrite gettemp()
in this way:
fn gettemp() -> f32 {
loop {
println!("What is your temperature?");
let mut temp = String::new();
io::stdin()
.read_line(&mut temp)
.expect("Failed to read the line");
match temp.trim().parse() {
Ok(temp) => return temp,
Err(_) => println!("Not a number!"),
};
}
}
IMHO explicit return temp
is much cleaner & readable (compared to suggested break out of the loop with a value).
A3 - Why we don't need to explicitly state <f32>
in temp.trim().parse()
It's inferred by the compiler.
fn gettemp() -> f32 { // 1. f32 is return type
loop {
println!("What is your temperature?");
let mut temp = String::new();
io::stdin()
.read_line(&mut temp)
.expect("Failed to read the line");
match temp.trim().parse() {
// 4. parse signature is pub fn parse<F>(&self) -> Result<F, ...>
// compiler knows it must be Result<f32, ...>
// Result<f32, ...> = Result<F, ...> => F = f32
// F was inferred and there's no need to explicitly state it
Ok(temp) => return temp,
// | |
// | 2. return type is f32, temp must be f32
// |
// | 3. temp must be f32, the parse result must be Result<f32, ...>
Err(_) => println!("Not a number!"),
};
}
}
Upvotes: 1
Reputation: 2376
In your program, loop { ... }
creates a new scope. The scope of the second temp
starts where it's defined and ends when loop
ends. See the following example:
fn main() {
let a = 1;
{
let a = 2;
println!("{}", a);
}
println!("{}", a);
}
This prints 2, 1。
If you want to return a string, use (the code is fixed according to the comment below):
fn gettemp() -> String {
loop {
let mut temp = String::new();
println!("What is your temperature?");
std::io::stdin().read_line(&mut temp).expect("Failed to read the line");
temp = temp.trim().to_string();
match temp.parse::<f32>() {
Err(_) => println!("Not a number!"),
_ => return temp,
}
}
}
&str
is a borrowed reference. You cannot return a borrowed reference to a local variable which will be released when the function returns.
Upvotes: 0
Reputation: 36071
Regarding question 1, you can break
out of the loop with a value:
fn gettemp() -> f32 {
let mut temp = String::new();
loop {
println!("What is your temperature?");
io::stdin().read_line(&mut temp).expect("Failed to read the line");
let temp = temp.trim().parse::<f32>();
if !temp.is_ok() {
println!("Not a number!");
} else {
break temp.unwrap() // yield value when breaking out of loop
}
}
}
This way, the whole loop's value is the thing you passed along with break
.
Regarding question 2, I am not sure if you really want to do this, because &str
is a borrowed type. I think you want to return an String
in this case which owns the data.
Upvotes: 1