Rust 结构无法替换 HashMap 中的特征
Rust struct can't replace trait in HashMap
我正在尝试创建一个 HashMap
包含特定输入的已知值。这个输入可以接受多种类型,只要它们实现了某种特征。然而,在这种情况下,只给出了 Rust 不喜欢的特定类型。
有什么方法可以将结构 "convert" 转化为特征,或者以其他方式解决这个问题?
#![allow(unused)]
use std::collections::HashMap;
use std::hash::*;
trait Element: Eq + PartialEq + Hash {}
trait Reaction<T: Element> {}
#[derive(Eq, Hash, PartialEq)]
struct Ion {
charge: u16
}
impl Element for Ion {}
#[derive(Eq, Hash, PartialEq)]
struct RedoxReaction<T: Element> { left: T }
impl<T: Element> Reaction<T> for RedoxReaction<T> {}
fn get_sep_database<T: Element>() -> HashMap<RedoxReaction<T>, f32> {
let mut map: HashMap<RedoxReaction<T>, f32> = HashMap::new();
let reaction = RedoxReaction {
left: Ion {
charge: 1
}
};
// note: expected type `RedoxReaction<T>`
// found type `RedoxReaction<Ion>`
map.insert(reaction, 0.000 as f32);
return map;
}
fn main() {
let db = get_sep_database();
let reaction = RedoxReaction {
left: Ion {
charge: 1
}
};
// expected this to be 0.000
let sep = db.get(&reaction);
}
这个问题的标准解决方案是使用特征对象而不是泛型。具体来说,RedoxReaction
将定义为:
#[derive(Eq, Hash, PartialEq)]
struct RedoxReaction { left: Box<Element> }
但是,这在这里不起作用,因为 PartialEq
、Eq
和 Hash
都不是 object safe。事实上,当其中一个是 Ion
而另一个是 Photon
.
时,询问两个 Elements
是否相等没有多大意义
我建议您考虑为您的元素使用 enum。您的所有元素都将具有相同的类型(枚举 Element
),并且对象安全不会成为问题。
我最终通过使用 Reaction
的 hash 作为 HashMap
的键来解决这个问题,函数如下:
use std::collections::hash_map::DefaultHasher;
use std::hash::*;
fn reaction_to_hash<E: Element>(reaction: &Reaction<E>) -> u64 {
let mut s = DefaultHasher::new();
reaction.hash(&mut s);
s.finish()
}
然后您可以使用 map.insert(reaction_to_hash(&reaction), <value>)
存储值,并使用 map.get(& reaction_to_hash(&reaction))
检索值。
我正在尝试创建一个 HashMap
包含特定输入的已知值。这个输入可以接受多种类型,只要它们实现了某种特征。然而,在这种情况下,只给出了 Rust 不喜欢的特定类型。
有什么方法可以将结构 "convert" 转化为特征,或者以其他方式解决这个问题?
#![allow(unused)]
use std::collections::HashMap;
use std::hash::*;
trait Element: Eq + PartialEq + Hash {}
trait Reaction<T: Element> {}
#[derive(Eq, Hash, PartialEq)]
struct Ion {
charge: u16
}
impl Element for Ion {}
#[derive(Eq, Hash, PartialEq)]
struct RedoxReaction<T: Element> { left: T }
impl<T: Element> Reaction<T> for RedoxReaction<T> {}
fn get_sep_database<T: Element>() -> HashMap<RedoxReaction<T>, f32> {
let mut map: HashMap<RedoxReaction<T>, f32> = HashMap::new();
let reaction = RedoxReaction {
left: Ion {
charge: 1
}
};
// note: expected type `RedoxReaction<T>`
// found type `RedoxReaction<Ion>`
map.insert(reaction, 0.000 as f32);
return map;
}
fn main() {
let db = get_sep_database();
let reaction = RedoxReaction {
left: Ion {
charge: 1
}
};
// expected this to be 0.000
let sep = db.get(&reaction);
}
这个问题的标准解决方案是使用特征对象而不是泛型。具体来说,RedoxReaction
将定义为:
#[derive(Eq, Hash, PartialEq)]
struct RedoxReaction { left: Box<Element> }
但是,这在这里不起作用,因为 PartialEq
、Eq
和 Hash
都不是 object safe。事实上,当其中一个是 Ion
而另一个是 Photon
.
Elements
是否相等没有多大意义
我建议您考虑为您的元素使用 enum。您的所有元素都将具有相同的类型(枚举 Element
),并且对象安全不会成为问题。
我最终通过使用 Reaction
的 hash 作为 HashMap
的键来解决这个问题,函数如下:
use std::collections::hash_map::DefaultHasher;
use std::hash::*;
fn reaction_to_hash<E: Element>(reaction: &Reaction<E>) -> u64 {
let mut s = DefaultHasher::new();
reaction.hash(&mut s);
s.finish()
}
然后您可以使用 map.insert(reaction_to_hash(&reaction), <value>)
存储值,并使用 map.get(& reaction_to_hash(&reaction))
检索值。