#How to prevent conflicting implemenations of from trait for e.g. closures

4 messages · Page 1 of 1 (latest)

last lily
#

Hello Rustaceans,

I'm currently rewriting parts of a test library and struggle with how to implement a function that takes multiple types of closure via Into for a part of it's API(either normal or async; either T or Result<T, Status>, where T implements a trait called Message. Unfortunately both Status and Message are provided by different crates).
I want to do this so that the testing crate is more flexible in how the user can use it(less necessary boiler plate; the logic will be handled inside).
I wrote some stub code that you can access here if you roughly want to see what I intend to do(apologies for the mess of generics, I'm planing to majorly refactor this later).
Unfortunately this fails due to conflicting impls for the From trait, probably because the compiler can't be sure that e.g. Message won't be implemented by Result<T, Status> etc I'm assuming?
implemenations
How could I describe such logic in Rust code to avoid the conflict and in general: How should I, in general when writing Rust, proceed when compilation fails due to Rust thinking that there may be multiple conflicting implementations?
I often can't think of a way to express 'this generic type implements either trait A or trait B, which are mutually EXCLUSIVE. (Sort of like an enum)' in Rusts typesystem.
(I would like to avoid choosing completely arbitrary types, like e.g. String, to implement a trait for as I've been too many times in the situation of having to find weird workarounds, because a library only implemented something for specific types to avoid having to deal with the type system. Hard to maintain, hard to reason through code and I'm still looking for a best practice that won't quickly turn into another footgun)

long forge
# last lily Hello Rustaceans, I'm currently rewriting parts of a test library and struggle...

in general, you can't have one impl for Fn() -> Resp for a generic Resp, and another for Fn() -> Fut for a generic Fut: Future<Output = Resp>, because those two impls do in fact overlap. what if a user wants to construct an FnClosure that returns a value that happens to implement Future (but they just want to return the future as is, without being awaited)?

however, if you are willing to introduce the additional restrictions of (1) only allowing specific response types that implement a certain trait, and (2) you're ok with introducing an additional marker type parameter which will need to be inferred at the call site, then you can use a solution like this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=db24fd4d60cb1649a1b2ea71cd6882bc
this avoids the problem of overlapping impls by defining a Response trait which you'll need to make sure to only implement for types that don't implement Future. but note that this approach can make type inference fail when upgrading to a new version of rust: in my example, if the standard library were to decide to add an impl Future for (), then it would overlap with my impl Response for (), which would surface as a "type annotations needed" error in main, because Marker can't be inferred

#

in practice, you're better off having a different function with a different name for each case

lusty radish