Odd trait bounds

How would you "read" (explain) these, in English?

where io:Error: From<MyError<T>>

where HashMap<T, usize, S>: FromIterator

I'll tackle the second one first since I know what the implementation looks like.

// The implementation
impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
where
    K: Eq + Hash,
    S: BuildHasher + Default,
// The bound you saw (added this --------v)
where HashMap<T, usize, S>: FromIterator<X>

The requirement of the bound is that HashMap<T, usize, S> implements FromIterator<X>.

You can interpret what this means for T, S, and X by lining up the "variables" between the where bound and the implementation, like algebra. We must have X = (K, V) since that's the only implementation of FromIterator<_> for HashMap<_, _, _>. And we must have K = T and V = usize (and S = S) due to how everything else lines up in the HashMap parameters.

The bounds on the implementation require

  • K: Eq + Hash
  • S: BuildHasher + Default

And so the where bound requires

  • T: Eq + Hash
  • S: BuildHasher + Default

As per usual, the function body can assume these bounds hold, and the function caller can only call the function if the bounds do hold.


where io::Error: From<MyError<T>>

For this version, there's presumably something like a

impl<T: /* Some conditions */> From<MyError<T>> for io::Error
// and maybe some additional
impl From<MyError<ConcreteType>> for io::Error

around. In which case either

  • T is one of the ConcreteTypes, or
  • whatever conditions are on T in the generic implementation are also enforced by the where bound

because in either of those cases, the requirement of the where bound will be met: io::Error implements From<MyError<T>>.[1]

Speaking generally, where clauses are conditions that must be met, like an algebraic formula, or a constraint satisfaction problem.. They don't have to be conditions on generic parameters directly. You can figure out the conditions for

17 < sqrt(X) + 2.4

even though I didn't write it like

X > 213.16...

And sometimes, there is no way to write the bounds such that only a generic parameter is on the left.[2]


  1. Both cases can't be true or there would be multiple implementations, which is not allowed ("coherence") ↩︎

  2. There is no way to write T: SomeBounds or T = ConcreteType for example. ↩︎

3 Likes

"io::Error is required to implement From<MyError<T>>".

What exactly is the "odd" part?

1 Like

Apologies if my Rust knowledge is not yet perfect. Yes, that's the simpler of the two, but I wasn't used to seeing a type param only on the RHS like that.

That's... not immediately obvious.

Thanks!