rusttraits

Rustlings Errors3.rs Convert From String Error - Trait Not Implemented for `std::string::String`


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

Solution

  • 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.