如何在Vec上更新或插入?

Dan*_*lme 5 rust borrow-checker

我在Rust写一个数据结构.它包含一Vec对键值对.当插入到结构中时,我需要找到匹配的键并更新键和值(实际上是子指针).代码看起来有点像这样,其中pivots是一个ref mutto Vec<Pivot>,Pivot它只是一个包含两个字段的结构:

match pivots.iter_mut().find(|ref p| key <= p.min_key) { // first mutable borrow
    Some(ref mut pivot) => {
        // If there is one, insert into it and update the pivot key
        pivot.min_key = key;
        pivot.child.insert(key, value) // recursive call
    },
    // o/w, insert a new leaf at the end
    None => pivots.push(Pivot /* ... */) // second mutable borrow
}
Run Code Online (Sandbox Code Playgroud)

但是有一个问题.即使我没有在第二部分中使用可变迭代器match,借用检查器也会抱怨我"不能*pivots一次多次借用可变的迭代".

这对我来说是完全合理的,因为第一次借用仍然在范围内,即使它没有在那种情况下使用match.这有点不方便:一个聪明的检查员当然可以说借用是不重叠的.我见过有人在线建议使用早期返回以避免问题,如下所示:

match pivots.iter_mut().find(|ref p| key <= p.min_key) {
    Some(ref mut pivot) => {
        pivot.min_key = key;
        pivot.child.insert(key, value);
        return
    },
    None => ()
};
pivots.push(Pivot /* ... */)
Run Code Online (Sandbox Code Playgroud)

但这似乎很难理解,特别是当它意味着将这些代码分解为自己的函数以允许return.是否有更惯用的方式来执行更新或插入操作?

Ste*_*fan 6

从长远来看,有一个合并的RFC "非词汇生命周期"解决了这个问题.使用Rust 2018中的非词法生命周期(Rust 1.31中提供),您的代码按原样运行:

操场

use std::collections::HashMap;

pub struct Pivot {
    pub min_key: u64,
    pub child: HashMap<u64, ()>,
}

fn update_or_append(pivots: &mut Vec<Pivot>, key: u64, value: ()) {
    match pivots.iter_mut().find(|ref p| key <= p.min_key) {
        Some(pivot) => {
            // If there is one, insert into it and update the pivot key
            pivot.min_key = key;
            pivot.child.insert(key, value);
            return;
        }
        // o/w insert a new leaf at the end
        None => {
            let mut m = HashMap::new();
            m.insert(key, value);
            pivots.push(Pivot {
                min_key: key,
                child: m,
            });
        }
    }
}

fn main() {
    let mut pivots = Vec::new();
    update_or_append(&mut pivots, 100, ());
}
Run Code Online (Sandbox Code Playgroud)

在Rust 2018之前,您可以通过一些额外的控制流处理来解决它.

bool无论更新是否发生,您都可以让匹配生成一个值,并在下面使用该值附加条件块.我考虑将"更新或追加"逻辑放入一个单独的函数(return在更新后使用)更惯用的方法:

操场

use std::collections::HashMap;

pub struct Pivot {
    pub min_key: u64,
    pub child: HashMap<u64, ()>,
}

fn update_or_append(pivots: &mut Vec<Pivot>, key: u64, value: ()) {
    if let Some(pivot) = pivots.iter_mut().find(|ref p| key <= p.min_key) {
        // If there is one, insert into it and update the pivot key
        pivot.min_key = key;
        pivot.child.insert(key, value);
        return;
    }
    // otherwise insert a new leaf at the end
    let mut m = HashMap::new();
    m.insert(key, value);
    pivots.push(Pivot {
        min_key: key,
        child: m,
    });
}

fn main() {
    let mut pivots = Vec::new();
    update_or_append(&mut pivots, 100, ());
}
Run Code Online (Sandbox Code Playgroud)

使用a bool来跟踪更新是否发生:

操场

use std::collections::HashMap;

pub struct Pivot {
    pub min_key: u64,
    pub child: HashMap<u64, ()>,
}

fn update_or_append(pivots: &mut Vec<Pivot>, key: u64, value: ()) {
    let updated = match pivots.iter_mut().find(|ref p| key <= p.min_key) {
        Some(pivot) => {
            // If there is one, insert into it and update the pivot key
            pivot.min_key = key;
            pivot.child.insert(key, value);
            true
        }
        // o/w insert a new leaf at the end below
        None => false,
    };
    if !updated {
        let mut m = HashMap::new();
        m.insert(key, value);
        pivots.push(Pivot {
            min_key: key,
            child: m,
        });
    }
}

fn main() {
    let mut pivots = Vec::new();
    update_or_append(&mut pivots, 100, ());
}
Run Code Online (Sandbox Code Playgroud)