Function to accept T or Option<T: impl Trait>

I need to define a function that accepts T or Option<T> parameter. The Into works for basic literals but fails on custom objects implementing a trait (structs).


pub fn foo<T: Into<Option<u32>>>(v: T) {
    v.into();
}

pub trait BarTrait {}

struct Bar {}
impl BarTrait for Bar {}

pub fn bar<T: Into<Option<impl BarTrait>>>(v: T) {
    v.into();
}

fn main() {
    foo(100u32); // OK
    foo(Some(100u32)); // OK
    foo(None); // OK
    bar(Bar{}); // OK
    bar(Some(Bar{})); // cannot infer type
    bar(None); // cannot infer type
}

Is there a hack for this?

If you don't mind me asking, why in the world would you want to feed either a T or an Option<T> into the same function? The whole purpose of an Option is to give you an 'option' to avoid providing anything, or to provide some value that has to be there.

If you need to have 3 scenarios for inputs, just define your own custom enum with 3 separate possible values - and use those as an input, don't hack around with the arguments of a function to trick it into accepting more than what's needed.

2 Likes

I imagine it's just for convenience so you don't have to wrap the argument in Some, the same way some std functions take Into<String> args.

I'm not sure there's any great workaround when the Option is generic. You get similar issues with just a t: Option<T> as well, when you pass a None the compiler doesn't know what the generic type parameter should be.

1 Like

Yes, it's just the "design" aspect and I'd like to make the interface easier for developers.

If you're willing to double-implement your trait for both T and Option<T> (macros can make it trivial), then it might look something like this:

pub fn foo<T>(v: T) where 
  T: Into<Option<u32>> {

    v.into();
}

pub trait BarTrait {}

struct Bar {}

impl BarTrait for Bar {}

impl BarTrait for Option<Bar> {}

pub fn bar<T>(v: T) where 
  T: BarTrait + Into<Option<T>> {

    v.into();
}

fn main() {
    foo(100u32); // OK
    foo(Some(100u32)); // OK
    foo(None); // OK
    bar(Bar{}); // OK
    bar(Some(Bar{})); // OK
    bar(None); // OK
}
3 Likes

@Ideator I totally forgot about that approach. I think this will work. The solution is so far the only way that covers cases with Vec and Option<Vec> variants that fail on From or TryFrom.

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.