What's so special about the i32 type here?

I haven't managed to minimize this yet, but github+cargo makes it pretty easy to reproduce:

git clone -b i32-inference-problem-demo https://github.com/dekellum/http
rustc --version
cargo test
cargo test --doc src/response.rs # more failures here

On our MSRV 1.39.0 and recent nightly as shown:

rustc 1.49.0-nightly (1773f60ea 2020-11-08)
error[E0277]: the trait bound `http::StatusCode: From<i32>` is not satisfied
  --> tests/status_code.rs:30:15
   |
30 |     let max = StatusCode::try_from(999).unwrap();
   |               ^^^^^^^^^^^^^^^^^^^^ the trait `From<i32>` is not implemented for `http::StatusCode`
   |
   = help: the following implementations were found:
             <http::StatusCode as From<&'a http::StatusCode>>
   = note: required because of the requirements on the impl of `Into<http::StatusCode>` for `i32`
   = note: required because of the requirements on the impl of `TryFrom<i32>` for `http::StatusCode`
   = note: required by `try_from`

error: aborting due to previous error

A workaround in tests/status_code.rs is to use 999u32. But why is that needed?

Or impl TryFrom<UT> for StatusCode and From<StatusCode> for UT for UT over all signed types (git revert d54d394).

More mysteriously, removing TryFrom<UT> and From<StatusCode> for UT, UT over u32, usize and u64 also avoids the issue? So adding only unsigned conversions can effectively be a breaking change to your public interface? See that change below:

https://github.com/dekellum/http/commit/3b90b73fcaa1eb33efc42fb8e0d8b0bff1705fc5

Anyone else been bitten by this or can deduce the cause?

Yup. This is the major part of why we haven't allowed array indexing by u8, for example -- right now a[4] knows the 4 is a usize because that's the only integer with an implementation (Index<usize>), but if we added Index<u8> then it'd go "wait, there's two? I'll fallback to i32 then" and start failing everything for there not being an Index<i32>.

Unfortunately you either need to pick just one integer type, or to basically allow all of them.

5 Likes

Rust will pick a specific integer type if there's some type inference which indicates which is intended. If there isn't, i32 is the fallback type for unconstrained integers.

When there's one possibility for the TryFrom, that will be the inference. If there is more than one possibility, though, it won't choose between them and ends up falling back to i32. If you add i32 to the list (without making the list complete), it will work (due to the fallback, not inference). But as @scottmcm said, better to do them all if sensible.

3 Likes

The type in question here, http::StatusCode is unsigned for its supported range, so that's why we initially picked support for all unsigned integer type conversions, and skipped the signed ones. Is there somewhere, literally, a "fallback to i32" for unannotated positive integer literals? Perhaps asked another way, why wouldn't rustc try usize or u64 or u32 before giving up with this error?

Thanks @quinedot, that adds the missing pieces for me.

Is there any known open RFC, issues or PRs that try to make inference work in the case of multiple TryFrom impls?

Not that I''m aware of (but I didn't go look either). Changing the fallback generally would be a breaking change, but I'm less sure about something like "I've narrowed the choice down to all unsigned integers and i32 won't compile, guess I'll go with u32 instead". I'd be interested to see what other people think.

2 Likes