Search code examples
rustcombinators

Rust chaining Results (Combinators)


I've been following a Rust tutorial where two versions of a function are purported to generate the same results:

Version 1:

pub fn get_transactions(fname:&str) -> Result<Vec<Transaction>,String> {
    let s = match std::fs::read_to_string(fname){
        Ok(v)=>v,
        Err(e)=> return Err(e.to_string()),
    };
    let t:Vec<Transaction> = match serde_json::from_str(&s) {
        Ok(v)=>v,
        Err(e)=> return Err(e.to_string()),
    };
    Ok(t)
}

Version 2:

fn get_transactions_b(fname:&str) -> Result<Vec<Transaction>,String> {
    std::fs::read_to_string(fname)
    .map_err(|e| e.to_string())
    .and_then(|ld| serde_json::from_str(&ld) )
    .map_err(|e| e.to_string())
}

However, I get the following message for version 2:

mismatched types

expected struct std::string::String, found struct serde_json::error::Error

note: expected enum std::result::Result<_, std::string::String> found enum std::result::Result<_, serde_json::error::Error>

help: try using a variant of the expected enum: _::_serde::export::Ok(serde_json::from_str(&ld)),


which I been unable to make head or tail out of:

  • Hovering over ld closure argument |ld| in VS Code it says it's of type std::string::String

  • Hovering over the ld in the from_str(&ld) I get the message.

  • Now I understand a Result is comprised of an Ok and an Err, but I thought the combinator chaining would have worked.

  • The compiler suggested fix doesn't work either.

    • (or make sense): What is the _::_ all about?
    • Why would the Ok not be inside the from_str?
  • What would you have to do to make version 2 work?

Here's the Result with the methods for combinators in the Rust docs.


Cargo.toml

[dependencies]
serde = "1.0.115"
serde_derive = "1.0.115"
serde_json = "1.0.57"

  • cargo 1.45.1
  • rustc 1.45.2

Solution

  • The problem comes from the and_then call.

    You are not allowed to change the error type when calling and_then. So your function there should return a Result<Vec<Transaction>, String>. However, the error type returned by serde_json::from_str is a serde_json::error::Error.

    You can fix it like this:

    std::fs::read_to_string(fname)
        .map_err(|e| e.to_string())
        .and_then(|ld| serde_json::from_str(&ld).map_err(|e| e.to_string()))
    

    Notice the call to map_err is now inside the function passed to and_then.