如何使外部递归类型可插入到 HashSet 中

发布于 2025-01-14 23:59:09 字数 2555 浏览 2 评论 0 原文

我使用 https://github.com/dtolnay/clang-ast 来解析生成的 JSON由 Clang 表示可用作 Rust 数据类型的 AST,特别是 Node。我想将树中的节点(Node 是递归结构)插入到 HashSet 中。我什至无法插入根注释:

use std::collections::HashSet;
use log::debug;
use std::env;
use serde::Deserialize;

pub type Node = clang_ast::Node<Clang>;

#[derive(Deserialize)]
#[derive(Debug)]
pub enum Clang {
    BinaryOperator(BinaryOperator),
    Other,
}

#[derive(Deserialize, Debug)]
pub struct BinaryOperator {
    pub opcode: String,
    pub range: clang_ast::SourceRange,
}

fn main() {
    env_logger::init();

    let json = std::fs::read_to_string("ast.json").unwrap();
    let node :Node = serde_json::from_str(&json).unwrap();
    let mut node_set = HashSet::new();
    node_set.insert(node);
}

这导致编译失败:

error[E0277]: the trait bound `clang_ast::Node<Clang>: Eq` is not satisfied
   --> src/main.rs:28:21
    |
28  |     node_set.insert(node);
    |              ------ ^^^^ the trait `Eq` is not implemented for `clang_ast::Node<Clang>`
    |              |
    |              required by a bound introduced by this call
    |
note: required by a bound in `HashSet::<T, S>::insert`
...

因此,我尝试使用添加 EqPartialEq 实现(遵循 如何为我自己的结构实现 Eq 和 Hash 以将它们用作 HashMap 键?):

impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for Node {}

但是这失败了与:

error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
 --> src/main.rs:8:1
  |
8 | impl PartialEq for Node {
  | ^^^^^---------^^^^^----
  | |    |             |
  | |    |             `clang_ast::Node` is not defined in the current crate
  | |    `clang_ast::Node` is not defined in the current crate
  | impl doesn't use only types from inside the current crate
  |
  = note: define and implement a trait or new type instead

我该如何进行这项工作?另外,为什么语言/编译器要施加这样的限制?

遵循为外来类型实现外来特征上的答案并没有真正帮助,因为有拼图的更多部分(实现 EqPartialEqHash 特征,处理递归性质clang_ast::注意)。

I use https://github.com/dtolnay/clang-ast to parse the JSON produced by Clang representing an AST to be available as a Rust data type, specifically the Node. I'd like to insert the nodes from the tree (Node<T> is recursive structure) into a HashSet. I could not even insert the root note:

use std::collections::HashSet;
use log::debug;
use std::env;
use serde::Deserialize;

pub type Node = clang_ast::Node<Clang>;

#[derive(Deserialize)]
#[derive(Debug)]
pub enum Clang {
    BinaryOperator(BinaryOperator),
    Other,
}

#[derive(Deserialize, Debug)]
pub struct BinaryOperator {
    pub opcode: String,
    pub range: clang_ast::SourceRange,
}

fn main() {
    env_logger::init();

    let json = std::fs::read_to_string("ast.json").unwrap();
    let node :Node = serde_json::from_str(&json).unwrap();
    let mut node_set = HashSet::new();
    node_set.insert(node);
}

this fails the compilation with:

error[E0277]: the trait bound `clang_ast::Node<Clang>: Eq` is not satisfied
   --> src/main.rs:28:21
    |
28  |     node_set.insert(node);
    |              ------ ^^^^ the trait `Eq` is not implemented for `clang_ast::Node<Clang>`
    |              |
    |              required by a bound introduced by this call
    |
note: required by a bound in `HashSet::<T, S>::insert`
...

So, I tried to add the Eq and PartialEq implementations using (following some advice from How to implement Eq and Hash for my own structs to use them as a HashMap key?):

impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for Node {}

however this fails with:

error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
 --> src/main.rs:8:1
  |
8 | impl PartialEq for Node {
  | ^^^^^---------^^^^^----
  | |    |             |
  | |    |             `clang_ast::Node` is not defined in the current crate
  | |    `clang_ast::Node` is not defined in the current crate
  | impl doesn't use only types from inside the current crate
  |
  = note: define and implement a trait or new type instead

How do I make this work ? Also, why does the language/compiler imposes such limit ?

Following the answers on Implement foreign trait for foreign type does not really help because there are more pieces to the puzzle (implementing Eq, PartialEq, the Hash trait, dealing with the recursive nature of clang_ast::Note<T>).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

夜灵血窟げ 2025-01-21 23:59:09

我该如何完成这项工作?

定义一个新类型包装器,例如 struct Node(clang:ast::Node)。

,或者在 clang_ast::Node 上实现该特征,而不仅仅是 clang_ast::Node1.

另外,为什么语言/编译器要施加这样的限制?

连贯性。结构的特征应该只有一种实现(忽略专业化)。允许板条箱在它们未定义的类型上实现它们未定义的特征,这可能会导致冲突和不连贯(其中一个依赖项使用一种实现,而另一个库使用另一种不兼容的实现),如果它们与以不同的方式处理相同的对象(例如某种通用集合,如哈希集)。

但这里按照 实现一致性规则只要没有 Clang 就是本地类型rel="nofollow noreferrer">未发现的类型 作为其参数之一出现,应该没问题(我无法在操场上测试它,因为 clang_ast 是那里的数字较小,但它与 Box 一起工作得很好)。

在其他上下文中(例如 Box重叠也可能是一个问题,但这里 clang_ast::Node 没有实现 PartialEq 根本没有,甚至没有条件。在那之前没有问题。


[1]:顺便说一句,您可能想报告错误消息具有误导性,因为 Node 是一个泛型类型,我认为 Rust 接受这种尝试是没有意义的。事实上,我无法重现它,在操场上尝试 impl PartialEq for Box 失败,并显示“错误[E0107]:缺少结构 Box 的泛型”。

编辑:构造出完全不正确的位,我认为一致性规则没有实际严格(仅适用于 BoxPin 和引用类型) 。

How do I make this work ?

Define a newtype wrapper e.g. struct Node(clang:ast::Node<Clang>).

, or implement the trait on clang_ast::Node<Clang> rather than just clang_ast::Node1.

Also, why does the language/compiler imposes such limit ?

Coherence. There should be only one implementation of a trait for a struct (ignoring specialisation). Allowing crates to implement traits they didn't define on types they didn't define is an opportunity for conflicts and incoherences (where one dependency uses one implementation and an other library uses an other incompatible implementation) which could lead to unsafety if they interact with the same objects in different ways (e.g. a generic collection of some sort, like a hashset).

Here though per the implementation coherence rules since Clang is a local type as long as there are no uncovered types appearing as one of its parameters it should be fine (I could not test this on the playground as clang_ast is to minor to figure there, but it works fine with Box).

In other contexts (like Box) overlapping can also be an issue, but here clang_ast::Node does not implement PartialEq at all, not even conditionally. Until that happens there's no issue.


[1]: incidentally you may want to report that the error message is misleading, since Node is a generic type I don't think it makes sense that Rust accepts this attempt. In fact I can not reproduce it, on the playground trying to impl PartialEq for Box fails with "error[E0107]: missing generics for struct Box".

edit: struct out the completely incorrect bits where I thought the coherence rules were less strict than they actually are (which is only the case for Box, Pin, and reference types).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文