0

I have encountered something that I don't understand about Result, From, and_then.

I have this function in my impl Parser that will give me either a slice of bytes or a ParseError when there are not enough bytes:

fn consume_bytes(self: &mut Parser<'a>, len: usize) -> Result<&[u8], ParseError> {
    // ...
}

I'm trying to define another function:

fn read_utf8(self: &mut Parser<'a>, len: usize) -> Result<String, ParseError> {
    self.consume_bytes(len)
        .and_then(|bytes| String::from_utf8(bytes.to_vec()))
}

This fails to compile:

error[E0308]: mismatched types
   --> src/parser.rs:147:31
    |
147 |             .and_then(|bytes| String::from_utf8(bytes.to_vec()))
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `parser::ParseError`, found struct `std::string::FromUtf8Error`
    |
    = note: expected type `std::result::Result<_, parser::ParseError>`
               found type `std::result::Result<std::string::String, std::string::FromUtf8Error>`

Because I have defined an implementation of From, I was expecting the conversion to be performed automatically, since by using the try! macro the conversion is automatic (from what I've understood):

impl From<FromUtf8Error> for ParseError {
    fn from(err: FromUtf8Error) -> ParseError {
        ParseError::InvalidConstantPoolEntry(err)
    }
}

This is another attempt that fails with the same error message:

fn read_utf8(self: &mut Parser<'a>, len: usize) -> Result<String, ParseError> {
    self.consume_bytes(len)
        .and_then(|bytes| String::from_utf8(bytes.to_vec()))
        .map_err(|e| From::from(e))
}

This version, where the map_err is inside the and_then lambda, works:

fn read_utf8(self: &mut Parser<'a>, len: usize) -> Result<String, ParseError> {
    self.consume_bytes(len)
        .and_then(|bytes| String::from_utf8(bytes.to_vec()).map_err(|e| From::from(e)))
}

Why doesn't and_then work as I expected?


PS: What's more idiomatic: the version I'm trying to write above or using the ? operator/the try! macro?

fn read_utf8(self: &mut Parser<'a>, len: usize) -> Result<String, ParseError> {
    let bytes = self.consume_bytes(len)?;
    Ok(String::from_utf8(bytes.to_vec())?)
}
3
  • 1
    you can just write: .map_err(From::from) Commented Feb 19, 2017 at 17:20
  • One question per post, please. Commented Feb 19, 2017 at 17:29
  • 1
    I will say that self: &mut Parser<'a> is not idiomatic, that's normally &mut self. Commented Feb 19, 2017 at 17:31

1 Answer 1

2

I was expecting the conversion to be performed automatically, since by using the try! macro the conversion is automatic (from what I've understood).

But you aren't using the try! macro!

fn read_utf8(self: &mut Parser<'a>, len: usize) -> Result<String, ParseError> {
    self.consume_bytes(len)
        .and_then(|bytes| String::from_utf8(bytes.to_vec()))
}

Code that you aren't calling cannot have any effect on your code, thankfully.


Review the signature for Result::and_then:

fn and_then<U, F>(self, op: F) -> Result<U, E> 
    where F: FnOnce(T) -> Result<U, E>

It takes a closure that returns a Result with the same error type that we started with. There is no automatic conversion of error types happening here. There may be some conversion of the success type, depending on what the closure chooses to do.

This is why your version that converts the error type inside and_then works, because you've converted the error type from FromUtf8Error to ParseError, making the error type returned by the closure match what and_then expects.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I missed the part about the signature. I've also taken a look at the real try! macro and now I think I understand much better.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.