Working around E207 "unconstrained type parameter"

I prepared a tiny playground where you can see the problem.

I have a trait DeserializeAs<'de, T> which can be implemented for any type. Now, I can create an implementation of it like

impl<'de, K, KAs, V, VAs> DeserializeAs<'de, Vec<(K, V)>> for BTreeMap<KAs, VAs>
where
    KAs: DeserializeAs<'de, K>,
    VAs: DeserializeAs<'de, V>,
{...}

The problem now arises, if I want to generalize the Vec type. The implementation would work for any type Seq: FromIterator<(K, V)>, because in the end I'm not using the fact that I have a Vec anywhere.
That mean, I would want to write

impl<'de, Seq, K, KAs, V, VAs> DeserializeAs<'de, Seq> for BTreeMap<KAs, VAs>
where
    KAs: DeserializeAs<'de, K>,
    VAs: DeserializeAs<'de, V>,
    Seq: FromIterator<(K, V)>,
{
    fn deserialize_as<D>(_deserializer: D) -> Result<Seq, D::Error>
    where
        D: Deserializer<'de>,
    {
        todo!()
    }
}

I read the error index, but could not find a good way to address this for my use case.
I cannot put the types K and V onto the implementing type, as I do not control that type. Putting it onto the implemented trait would work in principle, but would make using the trait much harder, as now they would always need to be specified, e.g., DeserializeAs<'de, Seq, (K, V)>. There is also no trait with an associated type I could use.

The problem with a dummy usage in the trait, is that additionally I would like to have a type like this

#[derive(Copy, Clone, Debug, Default)]
pub struct As<T>(PhantomData<T>);

impl<T> As<T> {
    pub fn deserialize<'de, D, I>(deserializer: D) -> Result<I, D::Error>
    where
        T: DeserializeAs<'de, I>,
        D: Deserializer<'de>,
    {
        T::deserialize_as(deserializer)
    }
}

which would then need to pass the dummy types through it as well, and any user of the As type would need to specify them manually since type inference wouldn't work here.

I would be happy to hear some ideas how this could be improved.

As some background for the types/traits. The code was suggested by @markazmierczak for serde#723, which covers improving UX for de/serialize_with inside container types. The proposed code works well for concrete types (like the Vec) but gets hard when generalizing (like the FromIterator).

Just as a quick suggestion: provide an API that just provides Iterator<(K, V)> such that implementing for all of the FromIterator<(K, V)> types becomes just delegating to the generic impl and collecting. It's not as optimal as applying one impl to all of the types, but it accomplishes the desired code sharing without too much impact

That is a good idea to reduce code duplication. It doesn't quite solve the problem. My main concern isn't with the data structures in std, since I can just exhaustively enumerate them all when implementing the trait, thus provide support for all the common lists, maps, and sets.

I would really love to be able to support more data structures out of the box though. Especially since that maps so nicely to the FromIterator trait.

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