porglezomp
porglezomp

Reputation: 1349

Is there a way to accept multiple arities of callback in Rust?

I'm trying to implement syntax sugar for my library so that someone can write .render(|image| { … }) instead of .render(|(), image| { … }) if they have no interesting state management going on. I thought I would be able to do this by implementing a trait for all FnMut(&State, &Image), and having a unit implementation for FnMut(&Image). Unfortunately, I get "conflicting implementation" errors when I try to implement this, because there's no reason something can't implement both of those FnMut traits.

My current attempt looks like this:

trait RenderCallback<State> {
    fn render(&mut self, state: &mut State, image: &mut Image);
}

impl<F, State> RenderCallback<State> for F
where
    F: FnMut(&mut State, &mut Image),
{
    fn render(&mut self, state: &mut State, image: &mut Image) {
        self(state, image)
    }
}

impl<F> RenderCallback<()> for F
where
    F: FnMut(&mut Image),
{
    fn render(&mut self, state: &mut State, image: &mut Image) {
        self(&mut (), image)
    }
}

Is there some way to achieve this effect?

Upvotes: 2

Views: 97

Answers (1)

zrzka
zrzka

Reputation: 21229

I think that the only way is to use auto traits with opt out if you want stick with this design. But it requires nightly. An example:

#![feature(optin_builtin_traits)]

// Implement DummyState for everything ...
auto trait DummyState {}
// ... but opt-out for ()
impl !DummyState for () {}

trait RenderCallback<State> {
    fn render(&mut self, state: &mut State, num: u8);
}

// Implement render callback for any type that implements DummyState
impl<F, State> RenderCallback<State> for F
where
    F: FnMut(&mut State, u8),
    State: DummyState, // DummyState means any type except opted out ()
{
    fn render(&mut self, state: &mut State, num: u8) {
        self(state, num)
    }
}

// Implement render callback for (), which doesn't implement DummyState,
// so there's no overlap
impl<F> RenderCallback<()> for F
where
    F: FnMut(u8),
{
    fn render(&mut self, _state: &mut (), num: u8) {
        self(num)
    }
}

fn with_state() {
    struct MyState {
        x: u8,
    };

    println!("with_state...");
    let mut state = MyState { x: 0 };
    let mut callback = |state: &mut MyState, num: u8| {
        state.x += num;
        println!("{}", state.x);
    };
    callback.render(&mut state, 1);
    callback.render(&mut state, 2);
    callback.render(&mut state, 3);
}

fn without_state() {
    println!("without state...");
    let mut callback = |num: u8| {
        println!("{}", num);
    };
    callback.render(&mut (), 1);
    callback.render(&mut (), 2);
    callback.render(&mut (), 3);
}

fn main() {
    with_state();
    without_state();
}

Upvotes: 1

Related Questions