Клонировать клонируемый элемент неклонируемого объекта в сопоставлении с образцом
Я работаю над проектом, который использует 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
сопоставьте возвращаемое значение и добавьте его на карту в конце, но я чувствовал, что могу также опубликовать вопрос и посмотреть, возможно ли что-то менее болезненное.
1 ответ
Вы можете использовать нелексические времена жизни, если для вас приемлемо использовать ночной компилятор.
#![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);
}
}