非标量演员:`Box <fnmut <&any>&gt;`

时间:2017-02-22 21:29:20

标签: casting rust traits

我正在尝试返回一个以&Any为参数的闭包。以下代码返回编译器错误。

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)>
    }
}

编译器错误如下:

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)>
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我错过了一些类型注释吗?

1 个答案:

答案 0 :(得分:4)

这里有一些问题。

首先,您尝试做的事情(从FnMut<&TSource, &mut TTarget>投射到FnMut<&Any, &mut Any>)并不有效。如果你成功了,你可以调用一个期望&TSource具有不同类型的函数 - 这样你就会破坏类型安全并导致未定义的行为。

要解决此问题,您可以将其包装在一个关闭内容中,该关闭会向下转换Any并处理任何错误(在此示例中,当我使用panic时它将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)
        }
    )

此时下一个问题变得明显:TypedSelector拥有原始的盒装闭包(select),但是这个新闭包需要访问它。有三种方法可以在Rust中传递值,但没有一种方法可以正常工作:

  • 按价值(移动)赢得工作,除非selector按值self获取(因此在此过程中将其销毁)
  • 不可变&reference不允许您拨打FnMut
  • 可变&mut reference同样不能从不可变的&self完成。

所以需要改变一些事情。我会随意选择功能最全但功能最重的选项,并使用Rc<RefCell<T>>分享指向内部可变FnMut的引用计数指针;这可能不是最适合您情况的选项:

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)>
}

Playground link