为什么unwrap_or()继续在范围内借用?

时间:2015-05-06 20:54:03

标签: rust

尝试编译:

fn main() {
    let mut vec = vec![1, 2, 3];
    let four: &mut u32 = borrow_or_add(&mut vec, 4);
}

fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 {
    vec.iter_mut().find(|v| **v == val).unwrap_or({
        vec.push(val);

        vec.last_mut().unwrap()
    })
}

Playground

...给出以下结果:

q.rs:8:9: 8:12 error: cannot borrow `*vec` as mutable more than once at a time
q.rs:8         vec.push(val);
               ^~~
q.rs:7:5: 7:8 note: previous borrow of `*vec` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*vec` until the borrow ends
q.rs:7     vec.iter_mut().find(|v| **v == val).unwrap_or({
           ^~~
q.rs:12:2: 12:2 note: previous borrow ends here
q.rs:6 fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 {
...
q.rs:12 }
        ^
q.rs:10:9: 10:12 error: cannot borrow `*vec` as mutable more than once at a time
q.rs:10         vec.last_mut().unwrap()
                ^~~
q.rs:7:5: 7:8 note: previous borrow of `*vec` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*vec` until the borrow ends
q.rs:7     vec.iter_mut().find(|v| **v == val).unwrap_or({
           ^~~
q.rs:12:2: 12:2 note: previous borrow ends here
q.rs:6 fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 {
...
q.rs:12 }
        ^
q.rs:10:9: 10:12 error: cannot borrow `*vec` as mutable more than once at a time
q.rs:10         vec.last_mut().unwrap()
                ^~~
q.rs:7:5: 7:8 note: previous borrow of `*vec` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*vec` until the borrow ends
q.rs:7     vec.iter_mut().find(|v| **v == val).unwrap_or({
           ^~~
q.rs:12:2: 12:2 note: previous borrow ends here
q.rs:6 fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 {
...
q.rs:12 }
        ^

据我所知,unwrap_or()的主体不能引用上述vec的借用,为什么不从范围中删除呢?有没有一种优雅的方式来实现这个功能?实际上,我无法在不使用两遍和bool(即contains())的情况下找到成功完成此操作的方法。

2 个答案:

答案 0 :(得分:7)

至少在整个声明中借用是有效的。不幸的是,将Option拉出到单独的语句中的通常解决方案将无济于事,因为它仍然包含一个可变引用(在Some中),它强制借用向量来扩展。使用find()时,这似乎是不可避免的。编译器担心指向Vector存储的指针在某处浮动,并且可能通过推入向量(这可能导致重新分配)而无效。这不仅包括返回的Option中的指针,还包括可由其中一个函数内部生成的指针。这是一个非常有效的问题,如果不是因为没有“隐藏”指针而你只在Option 不是指针时推送。生命周期系统的细微程度不足以捕获这一事实,我没有看到一种简单的方法来修复它或解决它。

您可以使用position来查找索引:

fn borrow_or_add(vec: &mut Vec<u32>, val: u32) -> &mut u32 {
    match vec.iter().position(|&v| v == val) {
        Some(i) => &mut vec[i],
        None => {
            vec.push(val);
            vec.last_mut().unwrap()
        }
    }
}

请注意,向量中的索引本质上是美化指针。如果向量发生变化,它们可以开始引用错误的元素,尽管它们不能在重新分配时变得悬空,而无效的索引(大于最大的有效索引)将导致恐慌而不是内存不安全。仍然,从position获取索引,然后使用它进行索引只是正确,因为矢量在其间没有(基本上)修改过。

答案 1 :(得分:0)

问题是凭借其语义知道vec.iter_mut().find().unwrap_or()会忽略它来自vec的任何内容。但是,编译器不知道这一点。实际上,方法调用链可以很好地存储来自迭代器的值,直到由unwrap_or()调用的闭包。请参阅此常见方案,该方案具有与您的示例类似的模拟结构:

let mut vec: Vec<i32> = vec![3, 2, 5];
vec.iter().map(|&i| { 
    vec.push(i+1)
}).last();

此处vec.iter_mut().find().unwrap_or()刚刚被vec.iter().map()取代。正如您所看到的,vec 中的值可以在整个方法调用中借用。在后一个例子中很明显vec.push(i+1)如何使迭代器无效,这就是编译器阻止它的原因。