簡體   English   中英

Rust Inspect Iterator:不能將`*`借用為不可變的因為它也被借用為可變的

[英]Rust Inspect Iterator: cannot borrow `*` as immutable because it is also borrowed as mutable

為什么我不能在inspect期間push送到這個向量並在skip_while期間contains它?

我為我自己的struct Chain實現了我自己的迭代器,如下所示:

struct Chain {
    n: u32,
}

impl Chain {
    fn new(start: u32) -> Chain {
        Chain { n: start }
    }
}

impl Iterator for Chain {
    type Item = u32;

    fn next(&mut self) -> Option<u32> {
        self.n = digit_factorial_sum(self.n);
        Some(self.n)
    }
}

現在,我想該做些什么呢take ,而迭代器產生獨特的價值觀。 所以我正在inspect鏈並推送到一個向量,然后在take_while范圍內檢查它:

let mut v = Vec::with_capacity(terms);
Chain::new(i)
    .inspect(|&x| {
        v.push(x)
    })
    .skip_while(|&x| {
        return v.contains(&x);
    })

但是,Rust編譯出了這個錯誤:

error: cannot borrow `v` as immutable because it is also borrowed as mutable [E0502]
...
borrow occurs due to use of `v` in closure
    return v.contains(&x);
           ^
previous borrow of `v` occurs here due to use in closure; the mutable borrow prevents subsequent moves, borrows, or modification of `v` until the borrow ends
    .inspect(|&x| {
        v.push(x)
    })

顯然我不理解“借用”的概念。 我究竟做錯了什么?

這里的問題是你試圖創建一個對同一個變量的可變和不可變引用,這違反了Rust借用規則。 而且,rustc確實非常清楚地對你這么說。

let mut v = Vec::with_capacity(terms);
Chain::new(i)
    .inspect(|&x| {
        v.push(x)
    })
    .skip_while(|&x| {
        return v.contains(&x);
    })

在這里,您嘗試在兩個閉包中使用v ,首先在inspect()參數中使用v ,在skip_while()參數中使用第二個閉包。 move閉包通過引用捕獲它們的環境,因此第一個閉包的環境包含&mut v ,第二個閉包的環境包含&v 閉包是在同一個表達式創建的,所以即使它是保證inspect()跑之前放棄了借skip_while()我是不實際的情況下,因為這是迭代器適配器,他們不會在所有運行直到迭代器被消耗),由於詞匯借用規則,這是禁止的。

不幸的是,當借用檢查器過於嚴格時,這就是其中一個例子。 您可以做的是使用RefCell ,它允許通過共享引用進行突變,但會引入一些運行時成本:

use std::cell::RefCell;

let mut v = RefCell::new(Vec::with_capacity(terms));
Chain::new(i)
    .inspect(|x| v.borrow_mut().push(*x))
    .skip_while(|x| v.borrow().contains(x))

認為有可能避免RefCell運行時懲罰並改為使用UnsafeCell ,因為當使用迭代器時,這些閉包只會一個接一個地運行,而不是同時運行,所以永遠不應該有一個mutable和一個不可變的引用同時出類拔萃。 它可能看起來像這樣:

use std::cell::UnsafeCell;

let mut v = UnsafeCell::new(Vec::with_capacity(terms));
Chain::new(i)
    .inspect(|x| unsafe { (&mut *v.get()).push(*x) })
    .skip_while(|x| unsafe { (&*v.get()).contains(x) })

但我可能是錯的,無論如何, RefCell的開銷並不高,除非這個代碼在一個非常緊湊的循環中運行,所以你應該只使用UnsafeCell作為最后的手段,只有在沒有其他工作時才使用,並且在使用時要格外小心使用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM