如何从特征对象获取Any:downcast_ref的所有权?

时间:2016-01-21 11:27:42

标签: rust

我遇到了与Rust的所有权规则和特征对象低迷的冲突。这是一个示例:

use std::any::Any;
trait Node{
    fn gen(&self) -> Box<Node>;
}

struct TextNode;
impl Node for TextNode{
    fn gen(&self) -> Box<Node>{
        Box::new(TextNode)
    }
}

fn main(){
    let mut v: Vec<TextNode> = Vec::new();
    let node = TextNode.gen();
    let foo = &node as &Any;
    match foo.downcast_ref::<TextNode>(){
        Some(n) => {
            v.push(*n);
        },
        None => ()
    };

}

TextNode::gen方法必须返回Box<Node>而不是Box<TextNode>,因此我必须将其转发为Box<TextNode>

Any::downcast_ref的返回值为Option<&T>,因此我无法获得向下转化结果的所有权并将其推送到v

====编辑=====

由于我不擅长英语,我的问题很模糊。

I am implementing(复制可能更精确)Go标准库中的模板解析器。

我真正需要的是一个向量Vec<Box<Node>>Vec<Box<Any>>,它可以包含TextNodeNumberNodeActionNode,任何类型的节点特质Node可以推入其中。

每个节点类型都需要实现copy方法,返回Box<Any>,然后向下转换为具体类型即可。但要复制Vec<Box<Any>>,因为你不知道每个元素的具体类型,你必须逐个检查,这是非常低效的。

如果复制方法返回Box<Node>,则复制Vec<Box<Node>>很简单。但似乎没有办法从特质对象中获取具体类型。

1 个答案:

答案 0 :(得分:6)

如果您控制trait Node,可以让它返回Box<Any>并使用Box::downcast方法

看起来像这样:

use std::any::Any;
trait Node {
    fn gen(&self) -> Box<Any>; // downcast works on Box<Any>
}

struct TextNode;

impl Node for TextNode {
    fn gen(&self) -> Box<Any> {
        Box::new(TextNode)
    }
}

fn main() {
    let mut v: Vec<TextNode> = Vec::new();
    let node = TextNode.gen();

    if let Ok(n) = node.downcast::<TextNode>() {
        v.push(*n);
    }
}

一般来说,您不应该跳转到使用Any。我知道它来自具有子类型多态性的语言并且想要重新创建具有某种根类型的类型的层次结构时看起来很熟悉(例如:在这种情况下:您尝试重新创建TextNode is a Node关系并创建一个{ {1}}节点)。我做了也做了很多其他人:我打赌Vec上的问题数量超过了Any实际上在crates.io上使用的次数。

虽然Any确实有其用途,但在Rust中它有其他选择。 如果你没有看过它们,我想确保你考虑这样做:

枚举

根据不同的节点类型,您可以表达&#34;节点是这些类型中的任何一种&#34;与枚举的关系:

Any

有了这个,你可以将它们全部放在一个struct TextNode; struct XmlNode; struct HtmlNode; enum Node { Text(TextNode), Xml(XmlNode), Html(HtmlNode), } 中,并根据变体做不同的事情,而不是向下转换:

Vec

playground

添加变体意味着可能在许多地方更改代码:枚举本身和使用枚举执行某些操作的所有函数(为新变体添加逻辑)。但是,再次,let v: Vec<Node> = vec![ Node::Text(TextNode), Node::Xml(XmlNode), Node::Html(HtmlNode)]; for n in &v { match n { &Node::Text(_) => println!("TextNode"), &Node::Xml(_) => println!("XmlNode"), &Node::Html(_) => println!("HtmlNode"), } } 它们大致相同,所有这些功能可能需要将向下转换添加到新变体。

特质对象(非任意)

您可以尝试在特征中的各种类型的节点上执行您想要执行的操作,因此您不需要向下转换,只需调用特征对象上的方法即可。 这基本上就是你正在做的事情,除了把方法放在Node trait而不是downcast上。

playground