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

Wan*_*iqi 3 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 => ()
    };

}
Run Code Online (Sandbox Code Playgroud)

TextNode::gen方法必须返回Box<Node>而不是Box<TextNode>,所以我必须将它转发给Box<TextNode>.

Any::downcast_ref的回报值是Option<&T>,所以我无法取得低估结果的所有权并推动它v.

====编辑=====

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

我正在 Go标准库中实现(复制可能更精确)模板解析器.

我真正需要的是一个载体,Vec<Box<Node>>或者Vec<Box<Any>>,它可以包含TextNode,NumberNode,ActionNode,实现该特性的任何类型的节点Node可以被推入到它.

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

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

Pao*_*lla 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

枚举

给定不同的Node类型,您可以使用枚举表达"节点是这些类型中的任何一种"关系:

struct TextNode;
struct XmlNode;
struct HtmlNode;

enum Node {
    Text(TextNode),
    Xml(XmlNode),
    Html(HtmlNode),
}
Run Code Online (Sandbox Code Playgroud)

有了它,您可以将它们全部放在一起Vec,根据变体做不同的事情,而不是向下转换:

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"),
    }
}
Run Code Online (Sandbox Code Playgroud)

操场

添加变体意味着可能在许多地方更改您的代码:枚举本身和使用枚举执行某些操作的所有函数(为新变体添加逻辑).但话说回来,由于Any它大致相同,所有这些功能可能需要将向下转换添加到新变体中.

特质对象(不是任何)

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

操场