I am trying to build a Fahrenheit to Celsius converter in Rust.
I compiled it successfully, but I don't know what went wrong in at runtime. Is this because of the conversion?
Here is my code:
use std::io;
fn main(){
println!("Please select\n 1.CELSIUS to FAHRENHEIT\n 2.FAHRENHEIT to CELSIUS");
const CEL_FAH: f64 = 32.00;
const FAH_CEL: f64 = -17.22;
let mut select = String::new();
io::stdin().read_line(&mut select)
.expect("Please select the appropriate options");
let select: i32 = select.parse().unwrap();
//control flow
if select == 1 {
println!("1. CELSIUS - FAHRENHEIT: ");
let cels = String::new();
let cels: f64 = cels.parse().unwrap();
let ans1 = cels * CEL_FAH;
println!("Conversions: {}", ans1);
} else if select == 2 {
println!("2. FAHRENHEIT - CELSIUS: ");
let fahs = String::new();
let fahs: f64 = fahs.parse().unwrap();
let ans2 = fahs * FAH_CEL;
println! ("Conversions: {}", ans2);
}else {
println!("Select the options please.");
}
}
Here is my output and error:
Compiling converter v0.1.0 (D:\Program Files\Rust Projects\converter)
Finished dev [unoptimized + debuginfo] target(s) in 2.46s
Running `target\debug\converter.exe`
Please select
1.CELSIUS to FAHRENHEIT
2.FAHRENHEIT to CELSIUS
2
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', src\main.rs:19:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\converter.exe` (exit code: 101)```
In your code, there are three mistakes:
When you're taking input using stdin() method and want to parse it into another type you have to put .trim() because stdin() adds whitespaces with your input so you need to trim those unwanted whitespaces.
Each time you are taking input you have to use io::stdin() method along with the read_line method so that the compiler hold for the user input.
You have used the wrong formula for conversion.
I have made the corrections in your code and it is working fine now, here is the snippet:
use std::io;
fn main() {
println!("Please select\n 1.CELSIUS to FAHRENHEIT\n 2.FAHRENHEIT to CELSIUS");
const CEL_FAH: f64 = 32.00;
const FAH_CEL: f64 = 1.8; //changed
let mut select = String::new();
io::stdin()
.read_line(&mut select)
.expect("Please select the appropriate options");
let select: i32 = select.trim().parse().unwrap(); // .trim() method requires when taking input
//control flow
if select == 1 {
println!("1. CELSIUS - FAHRENHEIT: ");
let mut cels = String::new();
io::stdin()
.read_line(&mut cels)
.expect("Please input a temperature in degrees"); // when you're taking input from user you have to use stdin()
let cels: f64 = cels.trim().parse().unwrap();
let ans1 = (cels * FAH_CEL) + CEL_FAH; //this the correct formula to convert from celsius to fahrenheit
println!("Conversions: {}", ans1);
} else if select == 2 {
println!("2. FAHRENHEIT - CELSIUS: ");
let mut fahs = String::new();
io::stdin()
.read_line(&mut fahs)
.expect("Please input a temperature in degrees"); //when you're taking input from user you have to use stdin()
let fahs: f64 = fahs.trim().parse().unwrap();
let ans2 = (fahs - CEL_FAH) * 5. / 9.; //this the correct formula to convert from fahrenheit to celsius
println!("Conversions: {}", ans2);
} else {
println!("Select the options please.");
}
}
You can also refer to this repository. Temperature converter rust
Your read_line also yielded a newline (the enter you pressed when selecting something). You first have to remove that from the input, for example like this:
select.truncate(select.len()-1);
Also, you have to specify the target-type that the string must be parsed to:
let select = select.parse::<i32>().unwrap();
(the rest of your snippet seems unfinished, so not responding to errors down there)
Related
I'm very newbie when it comes related to Rust, and I keep getting this error and honestly, I have no clue what's going on. I'm doing a Fahrenheit to Celsius, and the other way around.
Here is my code:
use std::io;
fn main() {
let mut choose = String::new();
println!("Choose between Celsius To Fahrenheit [1] or Fahrenheit to Celsius [2],\
please introduce a corrected value (integer)");
io::stdin()
.read_line(&mut choose)
.expect("Failed to read!");
// Careful with the trim, cuz it's removing all characters and it causes and error that basically, closes the CMD
// Carriage return BE CAREFUL MAN!
if choose.trim() == "1" {
println!("Please, Introduce a value ");
io::stdin().read_line(&mut choose).expect("Please, enter an integer");
let choose: i32 = choose.trim().parse().expect("Jjaanokapasao");
ctof(choose);
} else if choose.trim() == "2" {
println!("Please, Introduce a value");
io::stdin().read_line(&mut choose).expect("Please, enter an integer");
let choose: usize = choose.trim_end().parse().expect("Failed to convert to i32");
ftpc(choose);
}
}
fn ctof(c: i32) {
let celsius_to_fahrenheit: i32 = (c * (9 / 5)) + 32;
println!("Here is your conversion: {celsius_to_fahrenheit}")
}
fn ftpc(f: usize) {
let fahrenheit_to_celsius: usize = (f-32) * (5 / 9);
println!("Here is your conversion: {fahrenheit_to_celsius}")
}
'''
Using .read_line() to read into a String will append to the existing data, not overwrite it. And you used .trim() it ignore the newline in your comparisons, but it still exists; it wasn't removed from the string. So if you enter 1 and then 26, the variable choose will contain "1\n26\n". Using .trim() will not remove the newline character in the middle so .parse() will encounter an invalid digit.
You should call choose.clear() before writing into it again or else use a different variable.
When I run the program , It has no error, but when I run it, it gives result that I don't want
The Program(Rust):
use std::io;
fn main() {
println!("What temperature do you want to convert to?");
println!("Celcius to Fahrenheit?,Please Type cf");
println!("Fahrenheit to Celcius?,Please Type fc");
let mut the_answer = String::new();
io::stdin().read_line(&mut the_answer).expect("The Answer");
let x = String::from(the_answer);
println!("You want to convert to : {}", x);
println!("What temperature would you like to convert?");
let mut temperature = String::new();
io::stdin().read_line(&mut temperature).expect("The Number");
let temperature : i32 = match temperature.trim().parse(){
Ok(temperature) => temperature,
Err(_e) => {
-1
}
};
match x.as_str(){
"cf\n" => println!("{}", cf(temperature)),
"fc\n" => println!("{}", fc(temperature)),
_ => println!("temperature = {:?}", x),
}
}
fn cf(c: i32) -> i32 {
(c * (9/5)) + 32
}
fn fc(f: i32) -> i32 {
(f-32)*(5/9)
}
The Result:
Compiling Converter v0.1.0 (C:\Users\NicMar\Documents\Rust Projects\Converter)
warning: crate `Converter` should have a snake case name
|
= note: `#[warn(non_snake_case)]` on by default
= help: convert the identifier to snake case: `converter`
warning: `Converter` (bin "Converter") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
Running `target\debug\Converter.exe`
What temperature do you want to convert to?
Celcius to Fahrenheit?,Please Type cf
Fahrenheit to Celcius?,Please Type fc
cf
You want to convert to : cf
What temperature would you like to convert?
12
temperature = "cf\r\n"
the result is supposed to be temperature =53.6,
but it shows temperature = "cf\r\n"
Is there any mistake about my code? or I should change the version of the rust?
Like you can see, the problem is that the Enter at the end is not trimmed and so x is "cf\r\n" which is neither "cf" nor "fc", so we fall back to the default arm println!("temperature = {:?}", x).
You just need to .trim() it (or even .trim_end()):
let x = String::from(the_answer.trim());
this is my code. It takes a number and then panicks.
Code:
//Convert temperatures between Fahrenheit and Celsius.
use std::io;
fn main() {
let c: bool = true;
let f: bool = false;
let mut temperatur = String::new();
println!("Gib die Temperatur an:");
io::stdin()
.read_line(&mut temperatur)
.expect("Konnte nicht gelesen werden");
let temperatur_int: i32 = temperatur.parse::<i32>().unwrap();
println!("{}", temperatur_int);
}
Error:
Gib die Temperatur an: 5 thread 'main' panicked at 'called Result::unwrap()on anErrvalue: ParseIntError { kind: InvalidDigit }', src/main.rs:17:57 note: run withRUST_BACKTRACE=1 environment variable to display a backtrace
Tried to parse String to Integer
You're doing the right thing, but you forgot that you will get a newline in your string when reading from stdin. So instead of '32' you will have '32\n', which cannot be parsed.
So do trim() additionally before the parsing:
let temperatur_int: i32 = temperatur.trim().parse::<i32>().unwrap();
This question already has answers here:
Why does my string not match when reading user input from stdin?
(3 answers)
Closed 1 year ago.
Hello I'm trying to get user input using the get_input() which will return a String. get_input() is called by check_input() which uses match to check if the input String has only numerals or not and will return a bool depending on the input. I sliced the String and stored it as &str and even then my check_input() function returns false even if the input value was only numerals. Now if I hard code a &str and pass it in the match it returns true.
I'm trying to learn Rust and am very new to the lang (started Rust 3 days ago) so an in-depth explanation on what went wrong would be very welcome. And please give me any critiques/pointers you think one would need to get good at Rust.
CODE:
// // use std::io; // to take user input
use std::io; // to take user input
fn main() {
//let mut grid_size = get_input().trim().parse::<i64>().unwrap();
//println!("{}", grid_size + 2 );
println!("From main ---> {:?}",checkinput());
}
fn get_input() -> String{
// fn get_input() -> &'static str{
println!("Please enter the grid size" );
let mut input_string = String::new();
std::io::stdin().read_line(&mut input_string).expect("Failed");
return input_string;
// let my_own_str: String = input_string.to_owned();
// let sliced_str: &str = &my_own_str[..];
// println!("sliced_str ---> {}\nmy_own_string ---> {}", sliced_str, my_own_str);
// return sliced_str;
// let my_test_str: &str = "2";
// return my_test_str;
}
fn checkinput() -> bool{
// match get_input().bytes().all(|c| c.is_ascii_digit()) {
// let test = get_input().bytes().all(|c| c.is_ascii_digit());
// let test = get_input().chars().all(char::is_numeric);
let test_var = get_input(); // i get a String
let my_own_str: String = test_var.to_owned(); // i own the Strin
let sliced_str: &str = &my_own_str[..]; // i cut Strin into str
let sliced_str_new: &str = "123312"; // i cut Strin into str
// let sliced_str: &str = test_var.as_str(); // i cut Strin into str
// let sliced_str: &str = "123"; // if i put a str "123" then true
println!("sliced_str ---> {}", sliced_str); // print to check input val
println!("my_own_string ---> {}", my_own_str); // print to check input val
let test = sliced_str.chars().all(char::is_numeric); // check if my str is a numeric
println!("---------------------------------------");
println!("Type of my_own_str");
find_type(&my_own_str);
println!("---------------------------------------");
println!("Type of sliced_str");
find_type(&sliced_str);
println!("---------------------------------------");
println!("Type of sliced_str_new");
find_type(&sliced_str_new);
println!("---------------------------------------");
println!("TEST ---> {}", test); // print bool
match test {
true => return true,
false => return false,
}
}
fn find_type<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
add a .trim() before .chars():
let test = sliced_str.trim().chars().all(char::is_numeric);
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`
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!"),
};
}
}
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.
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.