我尝试使用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
函数都会有包含他们自己的向下倾斜。
答案 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
答案 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
的大小不合适。