Idea: Implement Eq for closures

When I write React component they often include properties that must implement Eq. This puts restrictions on closures that can be used for them. It can be easily workaround by implementing it manually:

// property that needs to have a callback

trait Validator {
    fn validate(&self, value: f32) -> bool;
}

#[component]
fn MyComponent<MC: MyCallback>(callback: MC) {
    // ...
}

// Usage

#[component]
fn DoSomething(min_val: f32) -> Element {
    #[derive(Clone, Eq, PartialEq)]
    struct BiggerThanMinVal {
        min_val: f32
    }
    impl Validator for BiggerThanMinVal {
        fn validate(&self, value: f32) -> bool {
            value > self.min_value
        }
    }
    rsx! {
        MyComponent {
            callback: BiggerThanMinVal { min_value }
        }
    }
}

There is a lot of boilerplate for meaning to have:

#[component]
fn MyComponent(callback: impl Fn() -> bool + Eq) {
    // ...
}

// Usage

#[component]
fn DoSomething(min_val: f32) -> Element {
    rsx! {
        MyComponent {
            callback: move |value| value > min_value
        }
    }
}

However currently closures do not implement Eq. It would be nice to implement Eq:

#[component]
fn DoSomething(min_val: f32) -> Element {
    rsx! {
        MyComponent {
            callback: move #[derive(Eq)] |value| value > min_value
        }
    }
}

That would desugar to deriving equality for closure structure.

1 Like

From the docs on function pointers (inside closures):

Note that while this type implements PartialEq, comparing function pointers is unreliable: pointers to the same function can compare inequal (because functions are duplicated in multiple codegen units), and pointers to different functions can compare equal (since identical functions can be deduplicated within a codegen unit).

I don’t think it’s a good idea to extend this to closures, even though we could. True, your reactive components wouldn’t break (though in the first case they’ll potentially refresh more than they would otherwise), but Eq is about a lot more than reactive code.

Once again I have mixed up closure types with function types; since every closure has a unique type, it really could just compare the captures. I’m still not sure it’s a good idea, but it’s less obviously a bad one.

3 Likes

I would note you still cannot compare between closures even within the proposal:

    let foo = #[derive(Eq)]  || true;
    let bar = #[derive(Eq)]  || true;
    // foo == bar does not compile because foo type and bar type are different

It only allows you to compare the same type:

    fn gen(val: bool) -> impl Fn() -> bool + Eq {
        #[derive(Eq)]  || val
    }
    let foo = gen(true);
    let bar = gen(true);
    // foo == bar

It would be enough for author's use case, as the same type of closure would get passed to components multiple times with different values of captures

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.