可变切片树

时间:2017-07-04 14:30:18

标签: rust

某些算法递归地将数组分成更小的部分。您可以构建一个由此类进程产生的显式二叉树,其中每个叶子都包含一个不相交的数组切片。如果您需要更改或重新排序叶子中的元素,则其切片必须是可变的。

enum Tree<'a> {
    Branch(Box<[Tree<'a>; 2]>),
    Leaf(&'a mut[f32]),
}

假设我需要拆分大于某个阈值的所有叶子。容易:递归地从树上走下树;当我找到一片足够长的叶片时,将它分成两半,将它们包裹成一个2叶子树,然后更换叶子。

impl<'a> Tree<'a> {
    fn split(&mut self, max: usize) {
        match self {
            &mut Tree::Branch(ref mut trees) => {
                trees[0].split(max);
                trees[1].split(max);
            },
            &mut Tree::Leaf(ref mut leaf) if leaf.len() > max => {
                let mid = leaf.len() / 2;
                let (l, r) = leaf.split_at_mut(mid);
                let trees = [Tree::Leaf(l), Tree::Leaf(r)];
                *self = Tree::Branch(Box::new(trees));
            },
        }
    }
}

不幸的是,借用检查器无法为ref mut leaf模式找到合适的生命周期。在允许分配到*self之前,它希望它已经死了,但我不知道如何从匹配组中进行分配。

如何使这项工作?

2 个答案:

答案 0 :(得分:3)

你可以使用两个技巧使它工作,这在代码中有解释。

use std::mem;

enum Tree<'a> {
    Branch(Box<[Tree<'a>; 2]>),
    Leaf(&'a mut[f32]),
    Placeholder, // it's not very nice hack, but it's required for mem::replace
}

impl<'a> Tree<'a> {
    fn split(&mut self, max: usize) {
        let mut needs_split = false;
        match self {
            &mut Tree::Branch(ref mut trees) => {
                trees[0].split(max);
                trees[1].split(max);
            },
            &mut Tree::Leaf(ref mut leaf) if leaf.len() > max => {
                // Postpone modification of *self. We can't do it now while
                // a part of *self is borrowed
                needs_split = true;
            },
            _ => {}
        }
        if needs_split {
            // move *self into cself, to be able to
            // deconstruct content, while keeping *self not borrowed
            let cself = mem::replace(self, Tree::Placeholder);
            if let Tree::Leaf(leaf) = cself {
                let mid = leaf.len() / 2;
                let (l, r) = leaf.split_at_mut(mid);
                let trees = [Tree::Leaf(l), Tree::Leaf(r)];
                *self = Tree::Branch(Box::new(trees));
            } else {
                unreachable!()
            }
        }
    }
}

答案 1 :(得分:2)

我不确定如何修复整个案例,但我或许可以引导你朝着正确的方向前进。首先,您需要明确self的生命周期:

fn split(&'a mut self, max: usize)

下一个问题是非详尽的比赛;这可以通过以下基本情况轻松避免(暂时):

_ => unimplemented!()

现在的问题是你在第一场比赛中两次可怜地借trees次;这可以通过使用循环来解决:

&mut Tree::Branch(ref mut trees) => {
    for tree in trees.iter_mut() {
        tree.split(max);
    }
}

现在你有:

error[E0506]: cannot assign to `*self` because it is borrowed
  --> <anon>:18:17
   |
14 |             &mut Tree::Leaf(ref mut leaf) if leaf.len() > max => {
   |                             ------------ borrow of `*self` occurs here
...
18 |                 *self = Tree::Branch(Box::new(trees));
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `*self` occurs here

可以通过不对匹配臂中的Leaf进行解构并使用辅助函数来避免这种情况:

impl<'a> Tree<'a> {
    fn get_trees(&'a mut self, max: usize) -> Option<[Tree<'a>; 2]> {
        if let &mut Tree::Leaf(ref mut leaf) = self {
            if leaf.len() > max {
                let mid = leaf.len() / 2;
                let (l, r) = leaf.split_at_mut(mid);
                Some([Tree::Leaf(l), Tree::Leaf(r)])
            } else {
                None
            }
        } else {
            None
        }
    }
}

&mut Tree::Leaf(_) => {
    let trees = self.get_trees(max).unwrap(); // safe (under a valid match arm)
    *self = Tree::Branch(Box::new(trees));
}

现在出现了一个更大的问题,因为在借用它时你无法分配给self。我会考虑使Clone能够(并根据trees的副本创建self),但在您的情况下,它不能自动导出;也许别人会有更好的主意。

Full code in the Rust playground