Type bounds issue with generic fn and concrete impl's

Looking for some basic guidance on how to approach the following: Rust Playground

I want to be able to write fn's that are generic over the Vendor trait, and have apply_value() accept any type that has an impl From for Value.

This seems basic but I am struggling to find the correct approach.

Thanks!

The code compiles if you literally apply the compiler's suggestion:

fn do_something<V: Vendor>(vendorized_thing: VendorizedThing<V>)
    where Value<V>: From<u32>
{
    ...
}

@H2CO3 Thank-you, yes, I am aware of that. However I want to be able to accept any type T that implements the appropriate conversion via From. So the list of bounds, such as From in your example, is essentially non-exhaustive.

The conversion implementations can be from downstream crates, etc.

I'm afraid I don't follow. What is the type T in this context? What is the actual problem you are solving? There are just too many layers of indirection here.

Basically, apply_value() should be able to accept any type (i.e. param "value: T"), that can be converted into a Value<V: Vendor>. However the "process of applying" is different for each vendor, so the impl of From is always for a concrete Vendor-implementing type. e.g. MyVendor in my example.

I want to allow downstream users to impl From for their own custom types, and provide a conversion to Value<MyVendor>, Value<MyOtherVendor>, or a Value for any other Vendor-implementing type.

I have a feeling that associated types may come into play here, but I can't quite figure it out. Thanks!

You describe how you want apply_value to work, and as far as I can tell, that's what you've implemented in the Playground. The disconnect seems to be here:

fn do_something<V: Vendor>(vendorized_thing: VendorizedThing<V>)
{
    vendorized_thing.apply_value(123u32);
}

Where you're saying this is inadequate because you want to cover all possible T and not just u32:

fn do_something<V: Vendor>(vendorized_thing: VendorizedThing<V>)
where
    Value<V>: From<u32> 
{
    vendorized_thing.apply_value(123u32);
}

But you're not using all or any possible types in this example, you're using u32 specifically. Naturally you won't be able to call apply_value(123u32) if there's not a supporting implementation for u32, so you need that bound. And you're not doing anything with any other T, so you don't need anything else. You could make the function generic over such a T:

fn do_something<V: Vendor, T>(vendorized_thing: VendorizedThing<V>)
where
    Value<V>: From<u32>, // Still needed!
    Value<V>: From<T>,
{
    vendorized_thing.apply_value(123u32);
}

But not only does this not help, it hurts, because you'll have to explicitly specify which T you mean when you call this function. If you're not going to make use of the T -- e.g. taking one as a parameter -- there's no reason to be generic over the T.

Perhaps your example is incomplete?

1 Like

Thanks, what you say makes total sense.

After looking at why it doesn't work as I expected, I suppose what I am hoping to achieve is essentially a way to infer the Value<V>: From<u32> bound from the V: Vendor bound itself. I understand that they are decoupled in my example, and that it does not work as is.

Any thoughts on how that could be done? It might be possible to introduce an associated type on the Vendor trait, that has explicit from_u32() methods, etc., but that will prevent conversion from other/custom types.

Are you trying to infer the bound (which is always satisfied and the same), or are you trying to infer some type (which may differ based on the Vendor)?


If you always need the bound Value<V>: From<u32> but don't want to type it all the time, you can require implementors of Vendor to supply the functionality:

trait Vendor: Sized {
    fn value_from_u32(value: u32) -> Value<Self>;
}

Then you can blanket implement the From implementation:

impl<V: Vendor> From<u32> for Value<V> {
    fn from(value: u32) -> Self {
        V::value_from_u32(value)
    }
}

Playground.


Associated types are for when you want a single type to be the "output" of an implementation. The type may vary per implementation, but there is only one per implementation. You can infer it from a generic bound like V: Vendor because it is the output of the implementation -- once you have a concrete V that implements Vendor, you can find the <V as Vendor>::AssociatedType.

I guess it could apply here if you didn't want to require conversion directly from a u32 per se for some reason.

trait Vendor: Sized {
    type Proxy: Into<Value<Self>> + From<u32>;
}

Then you could

fn do_something<V: Vendor>(vendorized_thing: VendorizedThing<V>) {
    let proxy = V::Proxy::from(123u32);
    vendorized_thing.apply_value(proxy);
}

Though this opens up the possibility that these two two entirely different things, which is a bit odd:

fn do_some_more_things<V: Vendor>(vt: VendorizedThing<V>)
where
    Value<V>: From<u32>,
{
    // Meows
    let proxy = V::Proxy::from(123u32);
    vendorized_thing.apply_value(proxy);
    // Barks
    vendorized_thing.apply_value(123u32);
}    

When writing that up, I realized there's probably a larger problem with your current design.


It seems that here

    fn apply_value<T: Into<Value<V>>>(&self, value: T) {
        let _ = value.into();
    }

You're wanting to rely on consumers of these types and traits to implement Vendor, and then implement From<T> for Value<MyType> (or Into<Value<MyType>> for T) for some arbitrary set of types which they choose. (Maybe with some being required, like u32.)

However, assuming these are in different crates and not all part of your own crate, they won't be able to. In the playground, Rc and Arc are standing in for your Value type. This demonstrates that a foreign type parameterized by a local one is still a foreign type, and thus the implementation cannot satisfy the orphan rules.

I suspect you're going to need to redesign how you want this to work. (If everything is going to be in a single crate, it may still work how you have it now.)

Are you trying to infer the bound (which is always satisfied and the same), or are you trying to infer some type (which may differ based on the Vendor )?

If I understand correctly, it is the bound that I want to infer. All I ever need to know is that the type T implements Into<Value<V: Vendor>>. I never need to the concrete type of T.

trait Vendor: Sized {
    fn value_from_u32(value: u32) -> Value<Self>;
}

Yes, this what I had in mind, but you are correct it doesn't not use associated types. Associated function is the proper term here I believe.

Regarding the orphan rules, again you are totally correct. In this case however, I have worked around it with a custom IntoValue trait in the actual project. This example was just my attempt to provide a minimal test case of what I was running into.

I will do some more experiments to see if I can rework things closer to what I want. Thanks for your help!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.