下面是一个更复杂的代码片段,其思想是加载一个SQL表,并设置一个hashmap,其中一个表结构字段作为键,并将结构作为值(实现细节并不重要,因为如果我克隆String
,代码工作正常,但是,数据库中的字符串可以任意长,克隆可能会很昂贵)。
以下代码将失败,
error[E0382]: use of partially moved value: `foo`
--> src/main.rs:24:35
|
24 | foo_hashmap.insert(foo.a, foo);
| ----- ^^^ value used here after partial move
| |
| value partially moved here
|
= note: partial move occurs because `foo.a` has type `String`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0382`.
use std::collections::HashMap;
struct Foo {
a: String,
b: String,
}
fn main() {
let foo_1 = Foo {
a: "bar".to_string(),
b: "bar".to_string(),
};
let foo_2 = Foo {
a: "bar".to_string(),
b: "bar".to_string(),
};
let foo_vec = vec![foo_1, foo_2];
let mut foo_hashmap = HashMap::new();
foo_vec.into_iter().for_each(|foo| {
foo_hashmap.insert(foo.a, foo); // foo.a.clone() will make this compile
});
}
结构Foo
不能实现Copy
,因为它的字段是String。我试着用Rc::new(RefCell::new())
Package foo.a
,但后来陷入了RefCell<String>
缺少特征Hash的陷阱,所以目前我不确定是否为结构体字段使用其他东西(Cow
能工作吗?),或者在for_each
循环中处理该逻辑。
1条答案
按热度按时间4nkexdtk1#
这里至少有两个问题:首先,生成的
HashMap<K, V>
将是一个自引用结构,因为K
借用了V
;在SA上有关于这个陷阱的many问题和答案。其次,即使你可以构造这样的HashMap
,你也很容易破坏HashMap
提供的保证,它允许你修改V
,同时假设K
总是保持不变:没有办法得到一个HashMap
的&mut K
,但你可以得到一个&mut V
;如果K
实际上是一个&V
,那么可以很容易地将K
修改为V
(通过改变Foo.a
的方式)并破坏Map。一种可能性是将
Foo.a
从String
更改为Rc<str>
,您可以以最小的运行时成本克隆它,以便将值放入K
和V
中。由于Rc<str>
是Borrow<str>
,您仍然可以通过&str
在Map中查找值。这仍然有理论上的缺点,你可以通过从Map中获取&mut Foo
和std::mem::swap
来破坏Map,这使得不可能从它的键中查找正确的值;但你得故意这么做另一种选择是实际使用
HashSet
而不是HashMap
,并为Foo
使用一个行为类似于Foo.a
的newtype。你必须像这样实现PartialEq
,Eq
,Hash
(以及Borrow<str>
):请注意,由于上述原因,
HashSet
无法获取&mut FooEntry
。您必须使用RefCell
(并阅读HashSet
的文档对此的说明)。第三个选项是简单地将
clone()
转换为foo.a
。鉴于上述情况,这可能是最简单的解决方案。如果使用Rc<str>
不会因为其他原因而困扰你,这将是我的选择。a
和/或b
,则Box<str>
比String
小一个机器字。