无法将实现Any的泛型类型转换为Any

时间:2015-12-29 12:49:32

标签: rust

我尝试使用Any存储动态对象。

我的问题是add_bar功能无法正常工作。我认为我可以将Bar<T> T: Any + 'static添加到Bar<Any>的向量中,但我得到不匹配的类型错误。

如果我按照here的建议,我会收到非标量广告错误。

use std::any::Any;

struct Bar<S: ?Sized> {
    ev: fn(S) -> bool,
}

// test case
struct Foo {
    pub hello: Vec<Bar<Any>>
}

impl Foo {
    pub fn add_bar<T: Any + 'static>(&mut self, baz: Bar<T>) {
        let baz2 = baz as Bar<_>; // non-scalar cast
        self.hello.push(baz2); // without using baz2: mismatched types. expected trait Any, found type parameter
    }
}

fn bla() {
    let mut my_foo = Foo { hello: Vec::new() };
    fn e(v: f64) -> bool {
        true
    }
    let my_bar = Bar::<f64> { ev: e };
    my_foo.add_bar::<f64>(my_bar);
}

到目前为止,唯一的解决方案是让ev函数以Any为参数,但这看起来并不优雅,因为所有可能的ev函数都会有包含他们自己的向下倾斜。

2 个答案:

答案 0 :(得分:3)

一般情况下,您可以Foo<T>投射到Foo<Any>(或至少是&Foo<T>&Foo<Any>等特征对象。< / p>

即。这是合法的:

struct Foo<T: ?Sized> { b: T }

fn main() {
    let b = Foo { b: 32 };
    let _b2: &Foo<Any> = &b;
}

法律是什么:

fn test(a: i32) { println!("{}", a+1); }
let t: fn(i32) = test;
//let t2: fn(Any) = t as fn(Any);
//                  ^~~~ this is the non-scalar cast

它不合法,因为它不是类型安全的,因为通常接受i32的函数不知道如何处理Any(如果你调用{ {1}}通过test,它如何在其身体中执行t2。)。

这就是为什么,正如你所说,使a+1成为ev会使你的程序编译。如果您允许fn(Any) -> bool采用fn,那么您确实强制每个Any包含其向下转换逻辑,但这是一个功能,而不是限制。顺便说一句(感谢@VladimirMatveev的评论),它需要是ev,因为fn(Box<Any>)是一个特征,你不能存储一个特性;您只能存储对它的引用并且具有trait object

那说,(虽然我不确定你的真实世界要求是什么)基于你的评论......

  

基本上,我想要一堆对象,每个对象都包含一个函数   可以使用一些内部状态,内部状态的类型不同

...为什么要将函数存储在struct中?向不同类型的环境添加行为的常用方法是为它们提供特征。我知道这可能不是你想要做的,但请跟我一点,我会在结尾添加一个例子,将fn存储在结构中。 现在,请注意(使用结构中的fn)你可以这样做:

Any

我不确定这是否适用于您的真实场景。即使您的示例代码,最显着的功能差异在于,在您的情况下,代码的客户端可以决定要附加到特定结构的行为,而使用特征方法则必须决定一个impl创建新的// environments are in the structs struct Bar1 { env1: String, env2: i32, } struct Bar2 { env1: char, } // behavior is in the trait trait Ev { // here I'm moving self in ev to reflect // what the fn does in your example code fn ev(self) -> bool; } // each impl can access the environment of its own struct impl Ev for Bar1 { fn ev(self) -> bool { (self.env2 == 42) } } impl Ev for Bar2 { fn ev(self) -> bool { (self.env1 == 'a') } } struct Foo { // Now we have a Vec of Ev trait objects instead of Any // Ev is a bare trait so we box it pub hello: Vec<Box<Ev>>, } impl Foo { // a T: Ev is any of Bar1, Bar2... pub fn add_bar<T: Ev + 'static>(&mut self, baz: T) { let baz2: Box<Ev> = Box::new(baz); self.hello.push(baz2); } } // example of use fn main() { let mut my_foo = Foo { hello: Vec::new() }; let my_bar = Bar2 { env1: 'a' }; my_foo.add_bar(my_bar); } 时的特征。

然而,在特征方法的基础上,如果你确实需要在结构中包含fn,那么现在可以在没有通用的情况下进行。你可以拥有

Bar

full example on the playground

答案 1 :(得分:1)

当你写let my_bar = Bar::<f64> { ev: e };时,你有

my_bar.ev: fn(f64) -> bool

您希望将其转换为Bar<Any>,但Bar<Any>

my_bar.ev: fn(Any) -> bool

显然你不能这样做,因为fn(f64) -> bool不能接受Vec<i32>,但Vec<i32>Any类型!这种行为没有意义! (严格地说,它主要没有意义,因为Any不是一种类型,而是一种特质。)

也许你想要一些你可以编写的BarAny类型

my_bar_any.downcast_ref::<f64>(): Option<&Bar<f64>>

这是明智的,但Any必须在另一个级别 - BarAny。事实上,BarAny只是Any的限制形式:

struct Foo {
    pub hello: Vec<Box<Any>>
}

impl Foo {
    pub fn add_bar<T: 'static>(&mut self, baz: Bar<T>) {
        self.hello.push(Box::new(baz));
    }
}

请注意,需要此框,因为Any的大小不合适。