Reputation: 352
I am trying to return a closure that takes an &Any
as argument. Following code returns a compiler error.
trait Selector {
fn id(&self) -> i64;
fn selector(&self) -> Box<FnMut(&Any, &mut Any)>;
}
struct TypedSelector<TSource, TTarget> {
id: i64,
select: Box<FnMut(&TSource, &mut TTarget)>,
}
impl<TSource, TTarget> Selector for TypedSelector<TSource, TTarget>
where TSource: 'static,
TTarget: 'static
{
fn id(&self) -> i64 {
self.id
}
fn selector(&self) -> Box<FnMut(&Any, &mut Any)> {
self.select as Box<FnMut(&Any, &mut Any)>
}
}
The compiler error is the following:
error: non-scalar cast: `Box<for<'r, 'r> std::ops::FnMut(&'r TSource, &'r mut TTarget) + 'static>` as `Box<for<'r, 'r> std::ops::FnMut(&'r std::any::Any + 'static, &'r mut std::any::Any + 'static)>`
--> src\main.rs:190:9
|
190 | self.select as Box<FnMut(&Any, &mut Any)>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Am I missing some type annotations?
Upvotes: 4
Views: 76
Reputation: 14041
There are a few problems here.
First, what you're trying to do (cast from FnMut<&TSource, &mut TTarget>
to FnMut<&Any, &mut Any>
) isn't valid. If you had succeeded you would have been able to call a function expecting a &TSource
with a different type - so you'd have broken type safety and caused undefined behaviour.
To fix this, you can wrap it in a closure which downcasts the Any
and handles any errors (in this example it will panic
as I use unwrap
):
Box::new(
move |a, b| {
let a = a.downcast_ref().expect("a was the wrong type.");
let b = b.downcast_mut().expect("b was the wrong type.");
(self.select)(a, b)
}
)
At this point the next problem becomes apparent: TypedSelector
owns the original boxed closure (select
), but this new closure needs access to it. There are three ways to pass values in Rust, and none work as is:
selector
takes self
by value (and hence destroys it in the process)&reference
wouldn't allow you to call the FnMut
&mut reference
similarly can't be done from the immutable &self
.So something needs to change. I'm going to arbitrarily pick the most fully featured but heavy-weight option, and use Rc<RefCell<T>>
to have shared reference-counted pointers to the internally-mutable FnMut
; this might not be the option which is best for your situation:
fn selector(&self) -> Box<FnMut(&Any, &mut Any)+'static> {
let wrapped = self.select.clone();
Box::new(
move |a, b| {
let a = a.downcast_ref().expect("a was the wrong type.");
let b = b.downcast_mut().expect("b was the wrong type.");
// Mutably borrow the shared closure (checked at runtime)
let mut f = wrapped.borrow_mut();
(&mut *f)(a, b)
}
)
//self.select as Box<FnMut(&Any, &mut Any)>
}
Upvotes: 4