I am working on the Rustlings course Errors3.rs:
// This is a program that is trying to use a completed version of the
// `total_cost` function from the previous exercise. It's not working though!
// Why not? What should we do to fix it?
use std::num::ParseIntError;
fn main() {
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
println!("You can't afford that many!");
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
}
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
Here is my current code:
use std::num::ParseIntError;
fn main() -> Result<String, String> {
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
//println!("You can't afford that many!");
Ok(format!("You can't afford that many!"))
} else {
tokens -= cost;
//println!("You now have {} tokens.", tokens);
Err(format!("You now have {} tokens.", tokens).to_string())
}
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
I am at a loss as to how to correct the first error:
error[E0277]: `?` couldn't convert the error to `std::string::String`
--> src/main.rs:7:46
|
7 | let cost = total_cost(pretend_user_input)?;
| ^ the trait `std::convert::From<std::num::ParseIntError>` is not implemented for `std::string::String`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= help: the following implementations were found:
<std::string::String as std::convert::From<&std::string::String>>
<std::string::String as std::convert::From<&str>>
<std::string::String as std::convert::From<std::borrow::Cow<'a, str>>>
<std::string::String as std::convert::From<std::boxed::Box<str>>>
= note: required by `std::convert::From::from`
error[E0277]: `main` has invalid return type `std::result::Result<std::string::String, std::string::String>`
--> src/main.rs:3:14
|
3 | fn main() -> Result<String, String> {
| ^^^^^^^^^^^^^^^^^^^^^^ `main` can only return types that implement `std::process::Termination`
|
= help: consider using `()`, or a `Result`
Based on the suggestions, regarding From
, I tried changing Ok(format!("You can't afford that many!"))
to Ok(String::from("You can't afford that many!"))
. But it results in virtually the same error message.
I have tried looking at the Rust documentation for std::convert::From. This gave me the idea of trying:
let slug: &'static str = "You can't afford that many!";
if cost > tokens {
//println!("You can't afford that many!");
Ok(std::convert::From(slug))
} else {
tokens -= cost;
//println!("You now have {} tokens.", tokens);
Err(format!("You now have {} tokens.", tokens).to_string())
}
Which results in the error:
error[E0423]: expected function, tuple struct or tuple variant, found trait `std::convert::From`
--> src/main.rs:12:12
|
12 | Ok(std::convert::From(slug))
| ^^^^^^^^^^^^^^^^^^ not a function, tuple struct or tuple variant
You tried to change the behaviour of the original program. The program must print something, not return a String from main()
(actually you can't return a String from main()
, you must return something that implements Termination
).
The solution is close to what you did; main()
must also return an error. There are two ways: use a real type or a dynamic trait. For your simple case, a real type is the easiest way:
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
println!("You can't afford that many!");
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
}
// we just return ok with nothing in it, this mean program terminated without error
Ok(())
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
You could also use a dynamic trait, it's more advanced but not particularly better:
use std::num::ParseIntError;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input)?;
if cost > tokens {
println!("You can't afford that many!");
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
}
Ok(())
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
There are lots of ways to handle errors in Rust, which you can learn more about in this blog post.