Create a function that receives either a ref or a closure

The following code shows a function that can accept either a struct or a closure; anyway, is there a way to modify it to accept a ref to the struct instead of the struct itself while still being able to pass a closure too?

pub trait MyTrait {}

pub struct MyStruct{}
impl MyTrait for MyStruct {}

impl <F> MyTrait for F where F: Fn() {}

pub fn caller<V: MyTrait>(data: V) {}

pub fn should_work_with_a_ref() {

    let one = MyStruct{}; 

    // Not OK
    caller(&one);  // <-- I would like this to compile

    // OK
    caller(one);   // <-- I don't need this

    // OK
    caller(|| {});

}

impl MyTrait for &MyStruct would make this work.

Of course, you can make both by value and reference work by implementing the trait for both MyStruct and &MyStruct.

1 Like

Ouch :flushed: that simple?!

Thank you very much!!

1 Like

If you want it to work for all references to things that implement MyTrait you can do something like

impl<T: ?Sized + MyTrait> MyTrait for &T {}
impl<T: ?Sized + MyTrait> MyTrait for &mut T {}

@KrishnaSannasi

interesting proposal, but it causes conflicting implementations issue:

 error[E0119]: conflicting implementations of trait `service::MyTrait` for type `&_`:
  --> core/src/service/mod.rs:15:1
   |
13 | impl <F> MyTrait for F where F: Fn() {}
   | ------------------------------------ first implementation here
14 | 
15 | impl<T: ?Sized + MyTrait> MyTrait for &T {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&_`

error[E0119]: conflicting implementations of trait `service::MyTrait` for type `&mut _`:
  --> core/src/service/mod.rs:16:1
   |
13 | impl <F> MyTrait for F where F: Fn() {}
   | ------------------------------------ first implementation here
...
16 | impl<T: ?Sized + MyTrait> MyTrait for &mut T {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&mut _`
   |
   = note: downstream crates may implement trait `std::ops::Fn<()>` for type `&mut _`

and, if I remove impl <F> MyTrait for F where F: Fn() {} it complains that:

error[E0277]: the trait bound `[closure@core/src/service/mod.rs:31:12: 31:17]: service::MyTrait` is not satisfied
  --> core/src/service/mod.rs:31:5
   |
31 |     caller(|| {});
   |     ^^^^^^ the trait `service::MyTrait` is not implemented for `[closure@core/src/service/mod.rs:31:12: 31:17]`
   |
note: required by `service::caller`

Yeah, if you have multiple blanket impls, they may not always work. In that case you will either need to specify references for each type.

Alternatively, you can make a newtype that implements MyTrait for closures like so

pub struct Func<F>(pub F);
impl <F> MyTrait for Func<F> where F: Fn() {}

But this may be too much of an ergonomic hit.