It's as if impl From<T> for T is sometimes not implemented. Compiler bug?

Hi! I needed to provide users of my API with a mechanism to change reqwest's RequestBuilder headers, so I started making a struct to pass to a user provided closure:

use reqwest; // 0.12.9
use reqwest::RequestBuilder;

pub struct RequestHeaderBuilder(RequestBuilder);

impl RequestHeaderBuilder {
	fn header_sensitive<K, V>(self, key: K, value: V, sensitive: bool) -> Self
	where
		reqwest::header::HeaderName: TryFrom<K>,
		<reqwest::header::HeaderName as TryFrom<K>>::Error: Into<http::Error>,
		reqwest::header::HeaderValue: TryFrom<V>,
		<reqwest::header::HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
	{
		let mut value: reqwest::header::HeaderValue = value.try_into().map_err(|e| "A".to_string()).unwrap();
		self.header(key, value)
	}

	fn header<K, V>(self, key: K, value: V) -> Self
	where
		reqwest::header::HeaderName: TryFrom<K>,
		<reqwest::header::HeaderName as TryFrom<K>>::Error: Into<http::Error>,
		reqwest::header::HeaderValue: TryFrom<V>,
		<reqwest::header::HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
	{
		Self(self.0.header(key, value))
	}
}

Lo and behold, this fails to compile with:

error[E0308]: mismatched types
  --> src/lib.rs:15:20
   |
7  |     fn header_sensitive<K, V>(self, key: K, value: V, sensitive: bool) -> Self
   |                            - expected this type parameter
...
15 |         self.header(key, value)
   |              ------      ^^^^^ expected type parameter `V`, found `HeaderValue`
   |              |
   |              arguments to this method are incorrect
   |
   = note: expected type parameter `V`
                      found struct `HeaderValue`
note: method defined here
  --> src/lib.rs:18:5
   |
18 |     fn header<K, V>(self, key: K, value: V) -> Self
   |        ^^^^^^                     --------

Given the blanket impls

impl<T, U> TryFrom<U> for T where U: Into<T>
impl<T> From<T> for T

I would expect that

impl<T> TryFrom<T> for T

is also implemented, so I don't know why this won't work. In any case, I tried simplifying a bit and tried:

use reqwest; // 0.12.9

pub trait HeaderBuilder
where
	Self: Sized,
{
	fn header_sensitive<K, V>(self, key: K, value: V, sensitive: bool) -> Self
	where
		reqwest::header::HeaderName: TryFrom<K>,
		<reqwest::header::HeaderName as TryFrom<K>>::Error: Into<http::Error>,
		reqwest::header::HeaderValue: TryFrom<V>,
		<reqwest::header::HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
	{
		let mut value: reqwest::header::HeaderValue = value.try_into().map_err(|e| "A".to_string()).unwrap();
		let v2 = reqwest::header::HeaderValue::try_from(value);
		todo!();
	}
}

and got

error[E0308]: mismatched types
   --> src/lib.rs:15:51
    |
7   |     fn header_sensitive<K, V>(self, key: K, value: V, sensitive: bool) -> Self
    |                            - expected this type parameter
...
15  |         let v2 = reqwest::header::HeaderValue::try_from(value);
    |                  -------------------------------------- ^^^^^ expected type parameter `V`, found `HeaderValue`
    |                  |
    |                  arguments to this function are incorrect
    |
    = note: expected type parameter `V`
                       found struct `HeaderValue`
note: associated function defined here
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:687:8
    |
687 |     fn try_from(value: T) -> Result<Self, Self::Error>;
    |        ^^^^^^^^

Ok, so impl<T> TryFrom<T> for T clearly is not working. And no, V can't definitely be the expected argument to anything anymore. Then I tried changing to

let v2 = reqwest::header::HeaderValue::from(value);

And it compiles! So impl<T> From<T> for T is working. In the midst of my attempt, I also tried

use reqwest; // 0.12.9

pub trait HeaderBuilder
where
	Self: Sized,
{
	fn header_sensitive<K, V>(self, key: K, value: V, sensitive: bool) -> Self
	where
		reqwest::header::HeaderName: TryFrom<K>,
		<reqwest::header::HeaderName as TryFrom<K>>::Error: Into<http::Error>,
		reqwest::header::HeaderValue: From<V>,
	{
		let mut value: reqwest::header::HeaderValue = value.into();
		let v2 = reqwest::header::HeaderValue::from(value);
		todo!();
	}
}

And now if failes to compile with

error[E0308]: mismatched types
   --> src/lib.rs:14:47
    |
7   |     fn header_sensitive<K, V>(self, key: K, value: V, sensitive: bool) -> Self
    |                            - expected this type parameter
...
14  |         let v2 = reqwest::header::HeaderValue::from(value);
    |                  ---------------------------------- ^^^^^ expected type parameter `V`, found `HeaderValue`
    |                  |
    |                  arguments to this function are incorrect
    |
    = note: expected type parameter `V`
                       found struct `HeaderValue`
note: associated function defined here
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:585:8
    |
585 |     fn from(value: T) -> Self;
    |        ^^^^

No, I'm sorry, you can't do that! You can't have

let mut value: reqwest::header::HeaderValue = ...;
let v2 = reqwest::header::HeaderValue::from(value);

work in some cases and others not, regardless of what's in the ... (except for errors caused by the ... itself, obviously)! And what's with the insistence on the expected type V!? It's a compiler bug, right? Right...?

There seems to be some odd type inference going on there. Explicitly instantiating the type parameters in the header call appears to work:

self.header::<K,reqwest::header::HeaderValue>(key, value)

TryFrom<T> for T is implemented, but there’s another detail in this code sample:

(Edit: quoted the wrong code the first time)

You have a bound that <HeaderValue as TryFrom<V>>::Error: Into<http::Error>.
The impl of TryFrom<U> for T where U: Into<T> has the error type std::convert::Infallible, which does not impl Into<http::Error>.

This should compile though:

let value: Type = ...;
let value2 = Type::from(value);

and IIRC it is a compiler bug caused by the HeaderValue: From<V> bound confusing the compiler (I remember it being mentioned on this forum somewhere, but I can’t find it now)

Very good point about the Error = Infallible. In the first code sample where I call self.header that is indeed an issue (EDIT: no it's not), but not in the second one (which happens to be the one you replied with). The input to HeaderValue::try_from is a HeaderValue which is a struct that has nothing to do with my where bounds.
@jameseb7 solution works indeed and doesn't affect the users.

I believe it's this or related. "I need some header_V where HeaderValue: TryFrom<header_V>, I know from the bounds that HeaderValue: TryFrom<header_sensitive_V>, guess that must mean header_V = header_sensitive_V", or such.

3 Likes