我正在开发一个项目,该项目使用BTreeMap
和自定义enum
来表示值。此enum
无法#[derive(Clone)]
,因为某些变体可能包含不具有Clone
能力的值。我的项目概述如下:
enum Foo {
// Bar has impl Clone, Baz does not.
// Both Bar and Baz are from external crates,
// so I cannot impl Clone on Baz.
A(Result<Vec<Bar>, Baz>),
B(Bar, Qux),
C,
}
struct Waldo {
map: BTreeMap<Bar, Foo>,
// Other variables...
}
在我的Waldo
- 类似物上定义一个方法,我遇到一种情况,在递归函数内部,我使用if let
对foo_map.get(&key)
的结果进行模式匹配;在此if
块中,我正在向map
添加值。当递归函数的后续迭代看到map
中的值时,他们知道他们可以忽略它。像这样:
impl Waldo {
fn do_something(&mut self, data: Bar) {
// analyze puts a Foo into map with `data` as a key.
// It can't return the value AND put it into the map, because
// Foo would need to be cloneable. Instead...
self.analyze(data);
// I let `do_something_else` put the value in the map,
// and then grab the value *from* the map.
if let Some(&Foo::A(Ok(ref bar_vec))) = self.map.get(&data) {
// bar_vec is cloneable, but even if I clone it,
// `self.map` is still borrowed.
// 'unique' is filtered so that it only contains
// elements of bar_vec that aren't a key in `self.map`
// 'unique' has no dependency on self.map,
// because the iterator clones all elements
// before collecting.
let unique = bar_vec
.iter() // &Bar
.filter(|b| !self.map.contains_key(b)) // &Bar, sans those in map
.cloned() // Bar
.collect<Vec<Bar>>()
// Give the newly encountered values a placeholder
// so that recursions of the function will ignore them
for new_item in unique.iter().cloned() {
self.map.insert(new_item, Foo::C); // fails
}
// Once the items are inserted with placeholder values,
// recurse the function to get their real values
for new_item in unique.into_iter() {
self.do_something(new_item);
}
}
fn analyze(&mut self, data: Xyzzy) {
// ...
}
}
理想情况下,我希望能够在我离开bar_vec
子句之前创建if let
的克隆,这意味着不再借用self.map
。这是可能的,还是我必须重构我的代码如何工作?我已经考虑过让analyze
返回Foo
枚举值而不是直接将其添加到地图中,并使do_something
与返回值匹配并将其添加到地图中的选项最后,但我觉得我也可以发布问题,看看是否有可能不那么痛苦。
答案 0 :(得分:2)
如果你可以使用夜间编译器,你可以使用非词汇生命周期。
#![feature(nll)]
use std::collections::BTreeMap;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
struct Bar;
// I cannot impl Clone on Baz
struct Baz;
enum Foo {
A(Result<Vec<Bar>, Baz>),
B(Bar),
C,
}
struct Waldo {
map: BTreeMap<Bar, Foo>,
// Other variables...
}
impl Waldo {
fn do_something(&mut self, data: Bar) {
self.analyze(data.clone());
if let Some(&Foo::A(Ok(ref bar_vec))) = self.map.get(&data) {
let unique = bar_vec
.iter()
.filter(|b| !self.map.contains_key(b))
.cloned()
.collect::<Vec<Bar>>();
for new_item in unique.iter().cloned() {
self.map.insert(new_item, Foo::C);
}
for new_item in unique.into_iter() {
self.do_something(new_item);
}
}
}
fn analyze(&mut self, data: Bar) {
unimplemented!()
}
}
在当前稳定的Rust中,你必须在self.map
范围之外改变if let
,因为借用是词法的,你不能在范围内“解除”变量。
fn do_something(&mut self, data: Bar) {
self.analyze(data.clone());
// This allows to access `unique` outside the scope
// where `self.map` is borrowed
let unique;
if let Some(&Foo::A(Ok(ref bar_vec))) = self.map.get(&data) {
unique = bar_vec
.iter()
.filter(|b| !self.map.contains_key(b)) // &Bar, sans those in map
.cloned()
.collect::<Vec<Bar>>();
} else {
// early return prevents the use of uninitialized `unique`
return;
}
for new_item in unique.iter().cloned() {
self.map.insert(new_item, Foo::C);
}
for new_item in unique.into_iter() {
self.do_something(new_item);
}
}