I know that Fn
can have Copy
and Clone
if all captured arguments implement either of those. But is it possible to get a closure which also implements Eq
and/or PartialEq
?
Why I want it? Think of DSL-like syntax based around closures whene some closures will take significant time to compute. I want to avoid recomputing a closure if it or its captured arguments haven't changed.
Some requirements / limitations:
- no user-side workarounds. I don't want to complicate public API and make it as seamless as possible
- all closures are
Fn
, because they must not change internal state as this can lead to different result - all closures are
'static
so they can't capture any references. this mostly comes from theAny
limitation - closures might have side-effects like modifying static variables and I don't care about those right now
- moving closure computation to a separate thread is not an option. I want to to avoid computation itself
Here is small example of Scope
from DSL:
use std::any::Any;
struct Scope {
cached_value: usize,
closure: Box::<dyn Any>,
}
impl Scope {
fn new<F>(f: F) -> Self
where F: Fn() -> usize + PartialEq + 'static
{
Self {
cached_value: f(),
closure: Box::new(f),
}
}
fn lazy_compute<F>(&mut self, f: F) -> usize
where F: Fn() -> usize + PartialEq + 'static
{
let Some(closure) = self.closure.downcast_mut::<F>() else {
self.cached_value = f();
self.closure = Box::new(f);
return self.cached_value;
};
// desired functionality here
if *closure == f {
return self.cached_value;
}
self.cached_value = f();
*closure = f;
self.cached_value
}
}
// 1. this is just a workaround for example to return the same closure type
// 2. I assume each returned closure from here will have the same type
fn build_closure(value: usize) -> impl Fn() -> usize {
move || value
}
fn main() {
let mut scope = Scope::new(|| 0);
assert_eq!(scope.lazy_compute(build_closure(42)), 42); // closure called
assert_eq!(scope.lazy_compute(build_closure(69)), 69); // closure called
assert_eq!(scope.lazy_compute(build_closure(69)), 69); // closure not called
}
Problem is that... Fn
doesn't implement PartialEq
.
So the question is there a way to get functionality that what I want?