Slightly confusing error message when implementing TryFrom

I'm currently writing a TFTP implementation to learn Rust.
Every operation involves an opcode which I defined as:

#[repr(u16)]
enum TftpOpcode {
    ReadRequest = 1,
    WriteRequest = 2,
    Data = 3,
    Acknowledgement = 4,
    Error = 5
}

At some point I get raw data over a UdpSocket, which I convert to u16 via the byteorder crate and I'd like to be able to create a TftpOpcode from that. For this reason I thought implementing TryFrom might be a good idea and copied the declaration from the docs:

impl TryFrom<u16> for TftpOpcode {
    type Error = &'static str;

    fn try_from(value: u16) -> Result<Self, Self::Error> {
        match value {
            1 => Ok(TftpOpcode::ReadRequest),
            2 => Ok(TftpOpcode::WriteRequest),
            3 => Ok(TftpOpcode::Data),
            4 => Ok(TftpOpcode::Acknowledgement),
            5 => Ok(TftpOpcode::Error),
            _ => Err("something went wrong")
        }
    }
}

The compiler doesn't like this and gives me an error message:

error: ambiguous associated item
   --> src\main.rs:65:45
    |
65  |     fn try_from(value: u16) -> Result<Self, Self::Error> {
    |                                             ^^^^^^^^^^^ help: use fully-qualified syntax: `<TftpOpcode as TryFrom>::Error`
    |

I read the hint and tried to apply the fix the compiler told me, which produced this error and confused me even more:

error[E0107]: wrong number of type arguments: expected 1, found 0
  --> src\main.rs:65:45
   |
65 |     fn try_from(value: u16) -> Result<Self, <TftpOpcode as TryFrom>::Error> {
   |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 type argument

It took me a lot of time to figure out that TryFrom needs its type argument just like in the impl block to compile properly. In my experience the compiler's hints have usually been helpful and following them fixed things most of the time.

My question is: Is it possible to have this error message include the type parameters to TryFrom so that one can literally follow (i.e. copy-paste) the compiler's suggestion?
If not, is it possible to better indicate the position where the type argument is missing? I was thinking something was wrong with my usage of Result first.

Do you by any chance have other impls for the same type that also have an associated Error type. For example another TryFrom with some different generic parameter?

In this case I'd probably just recommend to explicitly return Result<Self, &'static str>.

The problem is what you may implement TryFrom for other source types, for example:

impl TryFrom<bool> for TftpOpcode {
    type Error = !;
    // ..
}

Now Self::Error can be both &'static str and !. For better understanding view TryFrom<u16> and TryFrom<u32> as two separate traits like with generic types (Vec<u16> and Vec<u32> are two separate types).

So to solve your issue either state the error type explicitly or fully qualify the trait from which associated type will be taken as was proposed by compiler, like this: <Self as TryFrom<u16>>::Error.

No, I don't.
Actually I do have my own error type, but replaced it with &'static str for the sample/easier debugging.

I figured out how to compile this already; it is the compiler's suggestion that is misleading.

It was suggesting <Self as TryFrom>::Error instead of <Self as TryFrom<u16>>::Error, which, if you haven't been working a lot with traits (and I haven't been), is only slightly helpful and doesn't resolve the issue entirely.

Also, the fn declaration was copied out of the doc example. Maybe that needs to be updated because it's using Self::Error in its signature.

Result<Self, Self::Error> would work if you wouldn't have Error as a variant in your enum.

The compiler don't knows if you mean the variant or the associated type from the trait

You should report this as a bug including both error messages you got. Confusing/Misleading error messages are accepted as bugs. The devs care about helpful error messages and try to improve them where possible.

1 Like

This is the kind of answer I was looking for. Thanks, I'd have never noticed that.

I actually wanted to try this out myself and copy-pased the code from my first post, and, to my surprise, the compiler told me exactly the same:

   --> src\main.rs:13:5
    |
13  |     Error = 5
    |     ^^^^^^^^^
note: `Error` could also refer to associated type defined here

This was compiled on a different machine for a different target (stable-i686-pc-windows-msvc, rustc 1.40.0 (73528e339 2019-12-16)), which makes this even weirder.
On my home PC (which I currently don't have access to) I was using some nightly compiler (1.41 for x64 MSVC ABI I think). I'll post some more info about that when I'm back home.

oh also the compiler didn't tell you that you could write

<Self as TryFrom>::Error

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.