是否可以将struct插入地图中,其中键由所插入的值拥有?
在C中使用Hash-Maps时,这是我用来做的事情。
伪代码示例:
struct MyStruct {
pub map: BTreeMap<&String, StructThatContainsString>,
// XXX ^ Rust wants lifetime specified here!
}
struct StructThatContainsString {
id: String,
other_data: u32,
}
fn my_fn() {
let ms = MyStruct { map: BTreeMap::new() };
let item = StructThatContainsString {
id: "Some Key".to_string(),
other_data: 0,
}
ms.insert(&item.id, item);
}
如何正确处理这种情况?
如果不可能,可以完成相反的情况,该值在其中包含键的引用是
String
?一种替代方案可以是使用
set
代替map
,然后将整个struct
存储为键,但在比较时仅使用其值之一(似乎可以使用,但可以反火。如果您想在其他情况下比较struct
)。
它不会与普通参考作用:
let item = StructThatContainsString {
id: "Some Key".to_string(),
other_data: 0,
}
ms.insert(&item.id, item);
item
是移动进入地图,因此不能有任何待处理/参考。
另外,诸如get_mut()
之类的方法将变得危险或不可能,因为它可以修改具有出色参考的项目。
假设想要这样做的原因是节省空间,显而易见的选项是:
将密钥从值结构中取出。如果您同时需要它,则在地图中查找密钥时要么得到它,要么迭代器包括键和值:
:struct OnlyKey { id: String, } struct OnlyValue { other_data: u32, }
可以使用适当的方法来清理这一点。
在值的关键部分中使用诸如
Rc
之类的东西。Rc<T>
实现Ord
(BTreeMap
所需)如果T
。struct StructThatContainsString { id: Rc<String>, other_data: u32, }
使用struct的单个成员作为映射中的键可以(原则上)使用一个仅用于覆盖实现的零交叉包装结构的集合。
- 覆盖
Ord, Eq, PartialEq, PartialOrd
控制集合中的顺序。 -
覆盖
Borrow
因此BTreeSet.get(..)
可以采用用于订购的类型,而不是整个结构。 -
与此方法的一侧一侧是,将其添加到集合中时需要用容器包装结构。
这是一个有效的示例:
use ::std::collections::BTreeSet;
#[derive(Debug)]
pub struct MyItem {
id: String,
num: i64,
}
mod my_item_ord {
use super::MyItem;
#[derive(Debug)]
pub struct MyItem_Ord(pub MyItem);
use ::std::cmp::{
PartialEq,
Eq,
Ord,
Ordering,
};
use ::std::borrow::Borrow;
impl PartialEq for MyItem_Ord {
fn eq(&self, other: &Self) -> bool {
return self.0.id.eq(&other.0.id);
}
}
impl PartialOrd for MyItem_Ord {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
return self.0.id.partial_cmp(&other.0.id);
}
}
impl Eq for MyItem_Ord {}
impl Ord for MyItem_Ord {
fn cmp(&self, other: &Self) -> Ordering {
return self.0.id.cmp(&other.0.id);
}
}
impl Borrow<str> for MyItem_Ord {
fn borrow(&self) -> &str {
return &self.0.id;
}
}
}
fn main() {
use my_item_ord::MyItem_Ord;
let mut c: BTreeSet<MyItem_Ord> = BTreeSet::new();
c.insert(MyItem_Ord(MyItem { id: "Zombie".to_string(), num: 21, }));
c.insert(MyItem_Ord(MyItem { id: "Hello".to_string(), num: 1, }));
c.insert(MyItem_Ord(MyItem { id: "World".to_string(), num: 22, }));
c.insert(MyItem_Ord(MyItem { id: "The".to_string(), num: 11, }));
c.insert(MyItem_Ord(MyItem { id: "Brown".to_string(), num: 33, }));
c.insert(MyItem_Ord(MyItem { id: "Fox".to_string(), num: 99, }));
for i in &c {
println!("{:?}", i);
}
// Typical '.get()', too verbose needs an entire struct.
println!("lookup: {:?}", c.get(&MyItem_Ord(MyItem { id: "Zombie".to_string(), num: -1, })));
// ^^^^^^^ ignored
// Fancy '.get()' using only string, allowed because 'Borrow<str>' is implemented.
println!("lookup: {:?}", c.get("Zombie"));
println!("done!");
}
要避免必须手动定义这些定义,可以将其包裹到宏中:
///
/// Macro to create a container type to be used in a 'BTreeSet' or ordered types
/// to behave like a map where a key in the struct is used for the key.
///
/// For example, data in a set may have a unique identifier which
/// can be used in the struct as well as a key for it's use in the set.
///
///
/// ```
/// // Defines 'MyTypeOrd', a container type for existing struct,
/// // using MyType.uuid is used as the key.
/// container_order_by_member_impl(MyTypeOrd, MyType, uuid);
/// ```
///
/// See: http://stackoverflow.com/questions/41035869
#[macro_export]
macro_rules! container_type_order_by_member_struct_impl {
($t_ord:ident, $t_base:ty, $t_member:ident) => {
/// Caller must define the struct, see: container_type_order_by_member_impl
// pub struct $t_ord(pub $t_base);
impl PartialEq for $t_ord {
fn eq(&self, other: &Self) -> bool {
return (self.0).$t_member.eq(&(other.0).$t_member);
}
}
impl PartialOrd for $t_ord {
fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
return (self.0).$t_member.partial_cmp(&(other.0).$t_member);
}
}
impl Eq for $t_ord {}
impl Ord for $t_ord {
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
return (self.0).$t_member.cmp(&(other.0).$t_member);
}
}
impl ::std::borrow::Borrow<str> for $t_ord {
fn borrow(&self) -> &str {
return &(self.0).$t_member;
}
}
}
}
/// Macro that also defines structs.
#[macro_export]
macro_rules! container_type_order_by_member_impl {
(pub $t_ord:ident, $t_base:ty, $t_member:ident) => {
pub struct $t_ord(pub $t_base);
container_type_order_by_member_struct_impl!($t_ord, $t_base, $t_member);
};
($t_ord:ident, $t_base:ty, $t_member:ident) => {
struct $t_ord(pub $t_base);
container_type_order_by_member_struct_impl!($t_ord, $t_base, $t_member);
};
}