1

push中にこのベクトルにアクセスして、中にそれinspectを実行できないのはなぜですか?containsskip_while

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チェーンを -ing し、ベクトルにプッシュしてから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)
    })

明らかに、「借りる」という概念を理解していません。私は何を間違っていますか?

4

1 に答える 1

3

ここでの問題は、同じ変数への可変参照と不変参照の両方を作成しようとしていることです。これは、Rust の借用規則に違反しています。そして、rustcは実際にこれを非常に明確に言っています。

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

ここでは、最初の引数、2 番目の引数のv2 つのクロージャーで使用しようとしています。非クロージャは参照によって環境をキャプチャするため、最初のクロージャの環境には が含まれ、2 番目のクロージャの環境には が含まれます。クロージャーは同じ式で作成されるため、以前に借用を実行してドロップしたことが保証されていたとしても(これは実際のケースではありません。これらはイテレーター アダプターであり、イテレーターが消費されるまでまったく実行されないためです)。 、字句借用規則により、これは禁止されています。inspect()skip_while()move&mut v&vinspect()skip_while()

残念ながら、これは借用チェッカーが厳しすぎる例の 1 つです。できることは、を使用することです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))

イテレータが消費されると、これらのクロージャーは同時にではなく次々に実行されるため、実行時のペナルティを回避して代わりに使用することが可能であると思います。同時に。次のようになります。RefCellUnsafeCell

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.それ。

于 2016-04-09T07:27:16.663 に答える