英文:
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<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`.
I don't see an easy way around this without changing your struct. One way would be to replace the HashMap
s with another data structure which represents the same data but also implements Hash
, such as Vec<(String, String)>
. 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<(String, String)>
, 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论