当前位置: 首页 > 知识库问答 >
问题:

如何从trait对象获得任何:downcast_ref的所有权?

司英飙
2023-03-14

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

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

任何::downcast_ref的返回值是

==编辑=====

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

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

我真正需要的是一个向量,Vec

每个节点类型都需要实现复制方法,返回Box

如果复制方法返回Box


共有2个答案

夹谷衡
2023-03-14

解决问题的(更)意识形态方式:

use std::any::Any;

pub trait Nodeable {
    fn as_any(&self) -> &dyn Any;
}

#[derive(Clone, Debug)]
struct TextNode {}

impl Nodeable for TextNode {
    fn as_any(&self) -> &dyn Any {
        self
    }
}

fn main() {
    let mut v: Vec<Box<dyn Nodeable>> = Vec::new();
    let node = TextNode {};  // or impl TextNode::new

    v.push(Box::new(node));

    // the downcast back to TextNode could be solved like this:
    if let Some(b) = v.pop() {  // only if we have a node…
        let n = (*b).as_any().downcast_ref::<TextNode>().unwrap();  // this is secure *)
        println!("{:?}", n);
    };
}

*)这是安全的:只有nodables被允许降级到实现了nodable的类型。

梅玉堂
2023-03-14

如果你控制了trait Node,你可以让它返回一个Box

它看起来是这样的:

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是一个节点关系,并创建一个节点的Vec)。我也这么做了,其他很多人也这么做了:我打赌Any上的so问题数量超过了Any在板条箱上实际使用的次数。木卫一。

虽然任何代码都有它的用途,但在Rust中它有替代品。如果你没有看过它们,我想确保你考虑过这样做:

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

struct TextNode;
struct XmlNode;
struct HtmlNode;

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

有了它,您可以将它们全部放在一个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"),
    }
}

游戏场

添加变量意味着可能在许多地方更改代码:enum本身和所有使用enum的函数(为新变量添加逻辑)。但是,同样地,对于Any,它基本上是相同的,所有这些函数可能都需要向新的变体添加向下转换。

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

游戏场

 类似资料:
  • 问题内容: 我有一个具有@Embedded类Profile的User类。如何给Profile实例引用其所有者User类? 问题答案: 假设JPA而不是严格地处于hibernate状态,则可以通过应用getter / setter对而不是私有成员本身来实现。 但是,我想问的是,在这种情况下,嵌入式实体是否就是您想要的,而不是@OneToOne关系,或者只是将Profile类“展平”到User中。@E

  • 问题内容: 是否有任何内置函数可以返回对象的长度? 例如,我有哪个应该返回。如果我使用它返回。 它可能是一个简单的循环函数,但我想知道是否有内置函数? 问题答案: 对于支持Object.keys()的浏览器,您可以执行以下操作: 否则(特别是在IE <9中),您可以使用循环自己遍历对象: 该是有确保你只从字面对象计数的属性,而不是从它的原型属性是“继承”。

  • 如何从Firebase获取设备令牌(FCM注册令牌)? 我正在使用firebase制作移动应用程序,并使用Node.js制作其服务器。 当用户第一次运行应用程序并注册FCM服务器时,我是否应该将令牌保存到另一个数据库?

  • 问题内容: 我想知道实时有多少用户连接到我的应用程序。我想到了要打开的会话数循环的想法,但是我找不到该怎么做的方法。如果您有另一种方法可以提出您的建议。 问题答案: 到目前为止,我发现的最佳解决方案是计算会话的创建和销毁时间。 然后在VaadinServlet init()方法中添加SessionListeners

  • 而我需要的是提取这个:datanode、toto等。只有名称。 你能帮帮我吗? 我尝试使用jq,但没有成功。

  • 你好,我一直在尝试自动化监控一个站点,但它“响应”多个文档,我想知道如何浏览他们或选择哪一个我想要分析。 代码非常简单: 站点这样“回应”: 它不允许我发布图片,但这里有链接: 如何选择不同的单据而不是第一个单据?