如何从 trait 对象取得 Any:downcast_ref 的所有权? [英] How to take ownership of Any:downcast_ref from trait object?

查看:56
本文介绍了如何从 trait 对象取得 Any:downcast_ref 的所有权?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了与 Rust 的所有权规则和 trait 对象的冲突.这是一个示例:

I've met a conflict with Rust's ownership rules and a trait object downcast. This is a sample:

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 而不是 Box,所以我必须向下转换它到 Box.

The TextNode::gen method has to return Box<Node> instead of Box<TextNode>, so I have to downcast it to Box<TextNode>.

Any::downcast_ref 的返回值是 Option<&T>,所以我无法取得向下转换结果的所有权并将其推送到 v.

Any::downcast_ref's return value is Option<&T>, so I can't take ownership of the downcast result and push it to v.

====编辑======

====edit=====

因为我英语不好,所以我的问题很模糊.

As I am not good at English, my question is vague.

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

I am implementing (copying may be more precise) the template parser in Go standard library.

我真正需要的是一个向量,Vec>Vec>,它可以包含 TextNodeNumberNodeActionNode,任何类型的实现了Node特征的节点都可以被推入其中.

What I really need is a vector, Vec<Box<Node>> or Vec<Box<Any>>, which can contain TextNode, NumberNode, ActionNode, any type of node that implements the trait Node can be pushed into it.

每个节点类型都需要实现copy方法,返回Box,然后向下转换为具体类型就可以了.但是复制Vec>,由于不知道每个元素的具体类型,还得一一检查,效率很低.

Every node type needs to implement the copy method, return Box<Any>, and then downcasting to the concrete type is OK. But to copy Vec<Box<Any>>, as you don't know the concrete type of every element, you have to check one by one, that is really inefficient.

如果复制方法返回Box,那么复制Vec>很简单.但是好像没有办法从trait对象中得到具体的类型.

If the copy method returns Box<Node>, then copying Vec<Box<Node>> is simple. But it seems that there is no way to get the concrete type from trait object.

推荐答案

如果你控制 trait Node 你可以让它返回一个 Box 并使用 Box::downcast 方法

If you control trait Node you can have it return a Box<Any> and use the Box::downcast method

它看起来像这样:

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 关系并创建节点的 Vec).我也这样做了,许多其他人也这样做了:我敢打赌,Any 上的 SO 问题的数量超过了 crates.io 上实际使用 Any 的次数.

Generally speaking, you should not jump to using Any. I know it looks familiar when coming from a language with subtype polymorphism and want to recreate a hierarchy of types with some root type (like in this case: you're trying to recreate the TextNode is a Node relationship and create a Vec of Nodes). I did it too and so did many others: I bet the number of SO questions on Any outnumbers the times Any is actually used on crates.io.

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

While Any does have its uses, in Rust it has alternatives. In case you have not looked at them, I wanted to make sure you considered doing this with:

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

Given different Node types you can express the "a Node is any of these types" relationship with an enum:

struct TextNode;
struct XmlNode;
struct HtmlNode;

enum Node {
    Text(TextNode),
    Xml(XmlNode),
    Html(HtmlNode),
}

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

With that you can put them all in one Vec and do different things depending on the variant, without downcasting:

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

游乐场

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

adding a variant means potentially changing your code in many places: the enum itself and all the functions that do something with the enum (to add the logic for the new variant). But then again, with Any it's mostly the same, all those functions might need to add the downcast to the new variant.

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

You can try putting the actions you'd want to perform on the various types of nodes in the trait, so you don't need to downcast, but just call methods on the trait object. This is essentially what you were doing, except putting the method on the Node trait instead of downcasting.

游乐场

这篇关于如何从 trait 对象取得 Any:downcast_ref 的所有权?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆