在模式匹配中克隆非Cloneable对象的可复制元素

问题描述 投票:2回答:1

我正在开发一个项目,它使用BTreeMap和自定义enum值。这个enum不能#[derive(Clone)],因为某些变体可以包括一个不是Cloneable的值。我的项目概述如下:

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-analogue上定义一个方法,我遇到了一种情况,在递归函数内部,我使用if letfoo_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与返回值匹配并在结尾处将其添加到地图中,但我觉得我不妨发布问题,看看是否有可能减少痛苦。

rust borrow-checker
1个回答
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);
        }
    }

Playground link

© www.soinside.com 2019 - 2024. All rights reserved.