I have a case where I'd like to enable users to set a resolver. This resolver can be a simple function or a struct implementing a resolver. Here's an example:
// Parameter that each resolver accepts.
struct Intent {}
// Resolver A
type Pointer = fn(Intent) -> Result<usize, usize>;
// Resolver B
trait Resolver {
fn resolve(&self, intent: Intent) -> Result<usize, usize>;
}
// Desired final object
struct Handler<'a> {
resolver: Option<&'a dyn Resolver>,
pointer: Option<Pointer>,
}
impl <'a> Handler<'a> {
fn from_resolver(resolver: &'a dyn Resolver) -> Self {
Self { resolver: Some(resolver), pointer: None }
}
fn from_pointer(pointer: Pointer) -> Self {
Self { resolver: None, pointer: Some(pointer) }
}
}
impl <'a> From<&'a dyn Resolver> for Handler<'a> { // From anything implementing Resolver Into<Handler>
fn from(resolver: &'a dyn Resolver) -> Handler<'a> {
Handler::from_resolver(resolver)
}
}
impl <'a> From<Pointer> for Handler<'a> { // From Pointer Into<Handler>
fn from(pointer: Pointer) -> Handler<'a> {
Handler::from_pointer(pointer)
}
}
// Master struct accepting resolvers and converting them to Handler.
struct App<'a> {
handler: Option<Handler<'a>>,
}
impl <'a> App<'a> {
fn set_handler<H: Into<Handler<'a>>>(&mut self, handler: H) {
self.handler = Some(handler.into());
}
}
// The result I'd like is to be able to set Resolver trait or function pointer.
fn main() {
let app = App { handler: None };
// set structure
struct Foo {}
impl Resolver for Foo {
fn resolve(&self, intent: Intent) -> Result<usize, usize> { Ok(1) }
}
app.set_handler(&Foo{}); // ERROR
// or set function pointer / closure
fn bar(i: Intent) -> Result<usize, usize> { Ok(1) }
app.set_handler(bar); // ERROR
}
Obviously, I'm doing something wrong because I see errors on set_handler inputs.
the trait bound `Handler<'_>: std::convert::From<&main::Foo>` is not satisfied
the trait `std::convert::From<&main::Foo>` is not implemented for `Handler<'_>`
help: the following implementations were found:
<Handler<'a> as std::convert::From<&'a (dyn Resolver + 'a)>>
<Handler<'a> as std::convert::From<fn(Intent) -> std::result::Result<usize, usize>>>
note: required because of the requirements on the impl of `std::convert::Into<Handler<'_>>` for `&main::Foo`rustc(E0277)
main.rs(55, 9): the trait `std::convert::From<&main::Foo>` is not implemented for `Handler<'_>`
the trait bound `Handler<'_>: std::convert::From<fn(Intent) -> std::result::Result<usize, usize> {main::bar}>` is not satisfied
the trait `std::convert::From<fn(Intent) -> std::result::Result<usize, usize> {main::bar}>` is not implemented for `Handler<'_>`
help: the following implementations were found:
<Handler<'a> as std::convert::From<&'a (dyn Resolver + 'a)>>
<Handler<'a> as std::convert::From<fn(Intent) -> std::result::Result<usize, usize>>>
note: required because of the requirements on the impl of `std::convert::Into<Handler<'_>>` for `fn(Intent) -> std::result::Result<usize, usize> {main::bar}`rustc(E0277)
This comment is wrong. &'a dyn Resolver is not "anything implementing Resolver", it's the concrete type - the trait object, to be precise.
If you really want to allow converting every type which implements Resolver, you should specify this explicitly by using generics:
impl <'a, T> From<&'a T> for Handler<'a> where T: Resolver {
fn from(resolver: &'a T) -> Handler<'a> {
Handler::from_resolver(resolver as &_)
}
}
Note the as &_ bit: this is necessary to explicitly request conversion into trait object (you could also make Handler itself generic).
As for Pointer, there's another problem here: every function has its own, unique type, which is not equal to the function pointer type, but can be coerced into it - again, using as Pointer. If you don't like this, well, you'll have to use Fn* traits and, again, generics.
Instead of juggling with all sorts of complicated uses of trait objects and explicitly requiring lifetimes, you could just implement the trait for all suitable functions:
It means that you don't have to make a distinction between resolver "objects" and function "pointers", since it makes any function with type signature FnMut(Intent) -> Result<usize, usize> into a Resolver itself. So there's no need for a conversion. You can just start accepting any Resolver, and it will automatically work with functions too.
By the way, if you need "exactly one or the other, but not both"-style choices in the future, don't use a struct with optional fields. Use an enum instead.
@H2CO3 Hum, you gave me something to play with :). I can't get this to work for my case. Have you tested your solution in @Cerber-Ursi 's playground example? Can you modify it for me, please?