考虑为 `ItemChange` 添加 `#[derive(Hash)]` 注释,但它不起作用。

huangapple go评论76阅读模式
英文:

Consider annotating `ItemChange` with `#[derive(Hash)]` but it doesn't work

问题

以下是翻译好的部分:

我正在尝试创建一个具有已更改值的哈希映射的结构体。

示例:

#[derive(Debug, Clone, Eq, PartialEq)]
struct ItemChange {
    time: i64,
    metadata: HashMap<String, String>,
    changed_metadata: HashMap<String, String>,
    user_defined_description: String,
    description: String,
}

#[derive(Debug, Clone)]
pub struct ProductItem {
    id: String,
    /// 按日期排序的change_history
    change_history: HashMap<ItemChange, i64>,
}

impl ProductItem {
    pub fn add_change(&self, change: ItemChange, date: i64) {
        self.change_history.insert(); // <-- change_history没有insert :(
    }
}

代码无法编译,出现错误提示:

58 | struct ItemChange {
   | ----------------- 不满足`ItemChange: Hash`
...
97 |         self.change_history.insert();
   |                             ^^^^^^
   |
   = 注意:以下特质边界未满足:
           `ItemChange: Hash`
帮助:考虑用 `#[derive(Hash)]` 注解`ItemChange`

我做错了什么?

英文:

I am trying to create a struct that has a hashmap of changed values.

Example:

#[derive(Debug, Clone, Eq, PartialEq)]
struct ItemChange {
    time: i64,
    metadata: HashMap<String, String>,
    changed_metadata: HashMap<String, String>,
    user_defined_description: String,
    description: String,
}

#[derive(Debug, Clone)]
pub struct ProductItem {
    id: String,
    /// Ordered change_history by date
    change_history: HashMap<ItemChange, i64>,
}

impl ProductItem {
    pub fn add_change(&self, change: ItemChange, date: i64) {
        self.change_history.insert(); // <-- change_history doesn't have insert :( 
    }
}

The code doesn't compile with an error saying

58 | struct ItemChange {
   | ----------------- doesn't satisfy `ItemChange: Hash`
...
97 |         self.change_history.insert();
   |                             ^^^^^^
   |
   = note: the following trait bounds were not satisfied:
           `ItemChange: Hash`
help: consider annotating `ItemChange` with `#[derive(Hash)]`

What am I doing wrong?

答案1

得分: 2

以下是翻译好的内容,代码部分保持原文不变:

对于 HashMap 的键类型,必须实现 Hash trait。对于自定义的 struct,最简单的方式是使用 trait 的派生(derive)功能,但在这种情况下,会遇到其他问题。

正如 kmdreko 在他们的评论中提到的,问题在于 HashMap 没有实现 Hash trait,这意味着它不能作为键使用。因此,无法为具有 HashMap 字段的 struct 派生 Hash,因为 #[derive(Hash)] 要求每个字段都实现 Hash

像下面这样的一个简单示例会显示一个更有用的编译错误:

use std::collections::HashMap;

#[derive(Hash)]
struct TestStruct {
    hashmap: HashMap<u32, i32>,
}
error[E0277]: the trait bound `HashMap<u32, i32>: Hash` is not satisfied
 --> src/lib.rs:5:5
  |
3 | #[derive(Hash)]
  |          ---- in this derive macro expansion
4 | struct TestStruct {
5 |     hashmap: HashMap<u32, i32>,
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Hash` is not implemented for `HashMap<u32, i32>`
  |
  = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.

我认为在不改变你的 struct 的情况下很难解决这个问题。一种方法是将 HashMap 替换为另一种数据结构,该数据结构表示相同的数据,同时也实现了 Hash,比如 Vec<(String, String)>。根据如何使用哈希映射,这可能会影响性能,并且需要对所有与其交互的代码进行更改。

编辑:如果决定使用 Vec<(String, String)>,还需要确保该向量已排序,以便具有相同键-值对的两个向量具有相同的哈希值,如果元素的顺序不相同,则不会满足这一条件。

英文:

For a type to be used as a key for a HashMap, it must implement Hash. The easiest way to do this for your own struct is to derive the trait, however in this case you run into other issues.

As kmdreko mentioned in their comment, the problem is that HashMap doesn't implement the Hash trait, which means it cannot be used as a key. Therefore, you can't derive Hash on a struct that has a HashMap field because #[derive(Hash)] requires every field to be implement Hash.

A minimal example like this shows a more useful compiler error:

use std::collections::HashMap;

#[derive(Hash)]
struct TestStruct {
    hashmap: HashMap&lt;u32, i32&gt;,
}
error[E0277]: the trait bound `HashMap&lt;u32, i32&gt;: Hash` is not satisfied
 --&gt; src/lib.rs:5:5
  |
3 | #[derive(Hash)]
  |          ---- in this derive macro expansion
4 | struct TestStruct {
5 |     hashmap: HashMap&lt;u32, i32&gt;,
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Hash` is not implemented for `HashMap&lt;u32, i32&gt;`
  |
  = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.

I don't see an easy way around this without changing your struct. One way would be to replace the HashMaps with another data structure which represents the same data but also implements Hash, such as Vec&lt;(String, String)&gt;. This will likely have worse performance depending on how you use the hashmaps, and it will need changes to all the code which interacts with them.

Edit: If you decide to use Vec&lt;(String, String)&gt;, you will also need to ensure that the vec is sorted so that two vecs containing the same key-value pairs have the same hash, which is not the case if the elements are not ordered the same.

huangapple
  • 本文由 发表于 2023年2月20日 00:02:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75501452.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定