Making two function types implement a trait?

Why are these to impls "conflicting", and how do I get around this? What I'm trying to say seems logical: I have this trait called ClickListener and things that are Fn() or Fn(Event) implement it. Is it complaining because it's possible to have a type that implements both those traits, so then it wouldn't know which impl to use between 1 and 2 below? Maybe there is a way to tell it which one takes priority in that case?

playground link

#![allow(dead_code)]

#[derive(Debug)]
struct Event {
    foo: i32,
}
trait ClickListener {
    fn on_click(&self, event: Event);
}

// impl #1
impl<F: Fn(Event)> ClickListener for F {
    fn on_click(&self, event: Event) {
        (self)(event)
    }
}
// impl #2
impl<F: Fn()> ClickListener for F {
    fn on_click(&self, _event: Event) {
        (self)()
    }
}

struct ButtonBuilder {
    text: String,
    on_click: Option<Box<dyn ClickListener>>,
}

impl ButtonBuilder {
    fn text<S: Into<String>>(mut self, s: S) -> Self {
        self.text = s.into();
        self
    }
    fn on_click<F: ClickListener + 'static>(mut self, f: F) -> Self {
        self.on_click = Some(Box::new(f));
        self
    }
    fn new() -> Self {
        ButtonBuilder {
            text: "".to_string(),
            on_click: None,
        }
    }
}

fn main() {
    let _b = ButtonBuilder::new()
        .on_click(|e: Event| println!("click! e = {:?}", e));
    let _b = ButtonBuilder::new()
        .on_click(|| println!("click!"));
}

Error:

Compiling playground v0.0.1 (/playground)
error[E0119]: conflicting implementations of trait `ClickListener`
  --> src/main.rs:18:1
   |
12 | impl<F: Fn(Event)> ClickListener for F {
   | -------------------------------------- first implementation here
...
18 | impl<F: Fn()> ClickListener for F {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation

It's possible on unstable (and for less special traits in general), yes.

Supporting multiple applicable implementations with some sort of priority is a form of specialization. Stable Rust doesn't have specialization, and the unstable feature is far away from stabilization.

(In its current form I believe you would need a third implementation for types that implement both traits.)

1 Like

I just tried adding this, but it doesn't seem to help. I get the same conflict error.

impl<F: Fn(Event) + Fn()> ClickListener for F {
    fn on_click(&self, event: Event) {
        (self)(event)
    }
}

Specialization is not stable. You can't have potentially overlapping implementations on stable.

Even on unstable, I'm not sure if it supports this particular use case or not (and should have just not mentioned how I thought it works).


My practical advice is to keep the implementation for Fn(Event), and

     let _b = ButtonBuilder::new()
-        .on_click(|| println!("click!"));
+        .on_click(|_| println!("click!"));
1 Like

Ah, okay, got it. I misunderstood a bit of your previous post.

Thanks.