下面是一段更复杂的代码片段,其思想是加载一个 SQL 表并设置一个哈希图,其中一个表结构字段作为键,并将结构保留为值(实现细节并不重要,因为代码工作正常但是,如果我克隆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`.
Run Code Online (Sandbox Code Playgroud)
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`.
Run Code Online (Sandbox Code Playgroud)
该结构Foo无法实现,Copy因为它的字段是字符串。我尝试用 进行包装foo.a,Rc::new(RefCell::new())但后来陷入了缺少特征 Hash for 的陷阱RefCell<String>,所以目前我不确定是否使用其他结构字段(会Cow起作用吗?),或者在循环内处理该逻辑for_each。
这里至少有两个问题:首先,结果HashMap<K, V>将是一个自引用结构,如K借用V;SA 上有很多关于此陷阱的问题和答案。其次,即使您可以构造这样的 a HashMap,您也很容易破坏 提供的保证,这允许您在假设始终保持不变的情况下HashMap进行修改:没有办法为 a 获取 a ,但您可以获取 a ; if实际上是 a ,人们可以轻松地修改(通过 mutating )并破坏映射。VK&mut KHashMap&mut VK&VKVFoo.a
Foo.a一种可能性是从 a更改String为 a Rc<str>,您可以以最小的运行时成本进行克隆,以便将值同时放入 和K中V。现在Rc<str>,Borrow<str>您仍然可以通过 查找地图中的值&str。&mut Foo这仍然有一个理论上的缺点,即您可以通过从映射中获取 a 和std::mem::swap来破坏映射a,这使得无法从其键中查找正确的值;但你必须刻意这样做。
另一种选择是实际使用 aHashSet而不是 a ,并使用行为类似于 a 的HashMap新类型。你必须像这样实现, , (并且为了更好的措施):FooFoo.aPartialEqEqHashBorrow<str>
use std::collections::HashSet;
#[derive(Debug)]
struct Foo {
a: String,
b: String,
}
/// A newtype for `Foo` which behaves like a `str`
#[derive(Debug)]
struct FooEntry(Foo);
/// `FooEntry` compares to other `FooEntry` only via `.a`
impl PartialEq<FooEntry> for FooEntry {
fn eq(&self, other: &FooEntry) -> bool {
self.0.a == other.0.a
}
}
impl Eq for FooEntry {}
/// It also hashes the same way as a `Foo.a`
impl std::hash::Hash for FooEntry {
fn hash<H>(&self, hasher: &mut H)
where
H: std::hash::Hasher,
{
self.0.a.hash(hasher);
}
}
/// Due to the above, we can implement `Borrow`, so now we can look up
/// a `FooEntry` in the Set using &str
impl std::borrow::Borrow<str> for FooEntry {
fn borrow(&self) -> &str {
&self.0.a
}
}
fn main() {
let foo_1 = Foo {
a: "foo".to_string(),
b: "bar".to_string(),
};
let foo_2 = Foo {
a: "foobar".to_string(),
b: "barfoo".to_string(),
};
let foo_vec = vec![foo_1, foo_2];
let mut foo_hashmap = HashSet::new();
foo_vec.into_iter().for_each(|foo| {
foo_hashmap.insert(FooEntry(foo));
});
// Look up `Foo` using &str as keys...
println!("{:?}", foo_hashmap.get("foo").unwrap().0);
println!("{:?}", foo_hashmap.get("foobar").unwrap().0);
}
Run Code Online (Sandbox Code Playgroud)
请注意,由于上述原因,HashSet无法提供获得 a 的方法。&mut FooEntry您必须使用RefCell(并阅读文档HashSet对此的说明)。
第三种选择是简单地clone()按照foo.a您的描述进行操作。鉴于上述情况,这可能是最简单的解决方案。如果使用 anRc<str>不会因为其他原因打扰您,那么这将是我的选择。
旁注:如果不需要修改aand/or b,则 aBox<str>代替String会小一个机器字。
| 归档时间: |
|
| 查看次数: |
1041 次 |
| 最近记录: |