Клонировать клонируемый элемент неклонируемого объекта в сопоставлении с образцом

Я работаю над проектом, который использует 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);
        }
    }

Ссылка на игровую площадку

Другие вопросы по тегам