Workaround for E0117 rust error

I'll keep the story short so please don't comment with "why would you ever need something like this". I working with an encoder that encodes a type (e.g. T), vector of a type (e.g. Vec<T>), optional type (e.g. Option<T>), and optional vector of a type (e.g. Option<Vec<T>>). Each of the formats is encoded in a completely different way. The create I'm building provides the engine and users can define types T. Each T must implement a certain trait X.

I've built a "perfect" solution where a user adds 4 implementations:

use modulex::{InputBuffer, Input, Buffer, X};

// EXAMPLE 1
struct CustomType {}
impl InputBuffer<CustomType> for Input {} // OK
impl InputBuffer<Option<CustomType>> for Input {} // rustc E0117
impl InputBuffer<Vec<CustomType>> for Input {} // rustc E0117
impl InputBuffer<Option<Vec<CustomType>>> for Input {} // rustc E0117

// EXAMPLE 2
struct CustomType {}
impl From<CustomType> for Buffer {} // OK
impl From<Option<CustomType>> for Buffer {} // rustc E0117
impl From<Vec<CustomType>> for Buffer {} // rustc E0117
impl From<Option<Vec<CustomType>>> for Buffer {} // rustc E0117

Note that the Buffer, Input and InputBuffer are structures from the crate and CustomType is a structure defined by the user. The naming above is not important. I'd just like to show you how the concept works. Well, actually it does not. Rust complains with E0117 on Vec and Option type implementation.

Can this be solved?

Alternatively, is it possible to create an enum like below where T is the user's custom type:

enum Buffer<T: impl X> {
   Value(T),
   Option(Option<T>),
   Vec(Vec<T>),
   VecOption(Option<Vec<T>>),  
}

So the summary, a certain function must accept these 4 formats of a custom user-defined type that implements X, where X and the function are provided by the crate.

E0117 is the orphan rule: Only traits defined in the current crate can be implemented for arbitrary types.


Because modulex provides the InputBuffer trait, it needs to provide any blanket impls.

This would be acceptable, but it's not what you're trying to do:

impl<AnyUserType> InputBuffer for Option<AnyUserType>
  where AnyUserType: InputBuffer {
  //...

I think your only option is to flip the way the trait definitions work -- CustomType needs to be on the for side of the impl block.

Example 2 would instead provide trait IntoBuffer { fn into_buffer(self) -> modulex::Buffer; }.

1 Like

You can also try creating a wrapper for the implementing type

struct MyInput(Input);
impl InputBuffer<Vec<CustomType>> for MyInput {} // OK

The enum approach should work as well, at least with the details given so far. (though the syntax for trait bounds is a little off, it should be T: X).

@Heliozoa Could you please provide a working example for such enum? I can't make this to work correctly :frowning:.

pub enum Field<T> where T: IntoBuffer<T> {
    Value(T),
    Option(Option<T>),
    Vec(Vec<T>),
    VecOption(Option<Vec<T>>),  
}
impl From<> ...

Something like

impl<T: IntoBuffer<T>> From<Field<T>> for Buffer {}

I don't think it's easy to help you without more details about what the whole thing looks like in detail. E. g. I don't know if the choice to have plain type, vector, option, and vector of option, is a choice done by the user or if your modulex prescribes that it has to be those four. I also don't at all understand what you mean in the additional question

1 Like

Maybe I’m understanding this now after reading this sentence about 10 times, and guessing a few things. Since guesswork on my end was involved, what I suggest here might be way off and not helpful to you at all. If that’s the case please provide more details/context :wink:

So you’re having a function

fn foo(x: …)

that’s supposed to support types T or Option<T> or Vec<T> or Option<Vec<T>> where T can be any user-provided type as long as that type implements certain traits.

And your approach is to make the function signature

fn foo<T>(x: T) where Input: InputBuffer<T>

Assuming the choice of these four cases T, Option<T>, Vec<T>, Option<Vec<T>> is done by the modulex crate, you could just require implementation of a single trait with more methods. As long as the choice of modulex::Input is fixed, too, you could, for implementing a trait like

trait InputBuffer<T> {
    fn some_method(&self, x: T);
}

in the four variants, provide a helper trait

trait HelperTrait: Sized {
    fn some_method(this: &Input, x: Self);
    fn some_method_option(this: &Input, x: Option<Self>);
    fn some_method_vec(this: &Input, x: Vec<Self>);
    fn some_method_option_vec(this: &Input, x: Option<Vec<Self>>);
}

impl<T> InputBuffer<T> for Input where T: HelperTrait {
    fn some_method(&self, x: T) {
        T::some_method(self, x)
    }
}
impl<T> InputBuffer<Option<T>> for Input where T: HelperTrait {
    fn some_method(&self, x: Option<T>) {
        T::some_method_option(self, x)
    }
}
impl<T> InputBuffer<Vec<T>> for Input where T: HelperTrait {
    fn some_method(&self, x: Vec<T>) {
        T::some_method_vec(self, x)
    }
}
impl<T> InputBuffer<Option<Vec<T>>> for Input where T: HelperTrait {
    fn some_method(&self, x: Option<Vec<T>>) {
        T::some_method_option_vec(self, x)
    }
}

The user can implement the helper trait and this way gets the desired four impls.

If the choice of modulex::Input isn’t fixed, but there’s only finitely many possibilities, you could provide a separate helper trait for each of the types to choose from.

2 Likes

@steffahn You nailed it! Sorry if I did not articulate my questions well enough (it's 1 am ;)) but you understood exactly what I had in my mind. You taught me a new trick and it looks great and solves exactly the problem I have (had). Thank you so much!

Let me also thank all other souls in this thread. Your goodness is immeasurable.

2 Likes

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.