2016-09-12 13 views
11

に自己参照を持つ状態を初期化しカプセル化します以下のように(故意に冗長)main()にインラインで初期化される:を順次私は<code>RefCell</code>内に保持されて<code>Vec</code>のためのイテレータとして動作することができる構造体を定義しようとしている錆構造体

// Elided: lifetime parameter of Ref 
let vec_ref: Ref<Vec<i32>> = holds_vec.vec_in_refcell.borrow(); 
// Elided: lifetime parameter of Iter 
let mut vec_iter: Iter<i32> = vec_ref.iter(); 

の両方を保持しているIteratorを実装する構造体を定義する方法はあります(RefCellを残したままにする)とIter(のイテレータ状態を維持するため、Vecなどのイテレータをローリングするのではなく)、2番目のものが派生してから得られた参照を保持する最初?

私はこれを実装するためにいくつかの方法を試しましたが、すべては借用チェッカーと同じです。私は状態として裸構造体のメンバの両方の部分を入れた場合は、

struct HoldsVecInRefCellIter<'a> { 
    vec_ref: Ref<'a, Vec<i32>>, 
    vec_iter: Iter<'a, i32>, 
} 

のような、私はHoldsVecInRefCellIter { ... }構文(例えばDoes Rust have syntax for initializing a struct field with an earlier field?を参照)で一度に両方のフィールドを初期化することはできません。私は

struct HoldsVecInRefCellIter<'a> { 
    vec_ref: Ref<'a, Vec<i32>>, 
    vec_iter: Option<Iter<'a, i32>>, 
} 

// ... 

impl HoldsVecInRefCell { 
    // ... 

    fn iter(&self) -> HoldsVecInRefCellIter { 
     let mut new_iter = HoldsVecInRefCellIter { vec_ref: self.vec_in_refcell.borrow(), vec_iter: None }; 
     new_iter.vec_iter = new_iter.vec_ref.iter(); 
     new_iter 
    } 
} 

のような構造体で、順次初期化をシャントしようとすると、私はiter()から返す防止構造体の可変自己ボローが発生します。この構造体の自己借用は、構造体の一部を安全に動かすことができない構造体の一部に参照を格納しようとした場合にも発生します(Why can't I store a value and a reference to that value in the same struct?)。これと比較すると、HoldsVecInRefCellIterのような構造体のように見えますが、初期化を完了できれば、移動したときに正しいことが起こります。内部的にすべての参照は内部的にこの構造体よりも長いデータです。

は(https://internals.rust-lang.org/t/self-referencing-structs/418/3の例を参照)Rcを使用して、自己参照を作成しないようにするトリックがありますが、あなたは直接の参照を保持するために実装されている既存のIterator構造体を格納したい場合、私は、これらが適用される可能性がどのように表示されません基になるコンテナには、Rcではなく、

C++からのRust初心者として、これは頻繁に起きる問題のように感じられます(「コードブロックに複雑な状態の初期化ロジックがあり、そのロジックを抽象化して結果の状態を保持したい構造体に使用する」)。

関連質問:Returning iterator of a Vec in a RefCell

+0

複雑な初期設定が一般的である - それは、通常、ビルダーパターンによって解決です。それはここでは問題ではありません。スライスの反復子は、存在する時間全体にわたってスライスへの参照を保持できることを期待しています。実際には、自分自身への参照を生成するイテレータを書くことはできません(http://stackoverflow.com/questions/25702909/can-i-write-an-iterator-that-yields-a-reference-into-itself)。 – Shepmaster

+0

保存することができる 'fn borrow(&self) - > Ref >'メソッドを追加して、 'iter'をonにするか、イテレータを提供するクロージャを受け入れることができます:' fn iter (&self、f:F) ' – Shepmaster

+0

明確にする:私の意図は、イテレータが値を返すことであって、参照(例コードの' i32')ではない。私の挑戦は、基礎となるコンテナのための 'Iterator'を格納することによって値を生成することができるイテレータ構造体を初期化することでした。この例では、 'HoldsVecInRefCellIter'は' Ref > 'とベクトルへのインデックスだけを保持できますが、他の' Ref > 'はコンテナの' Iterator'を望むと思います。 –

答えて

6

私たちは、カンニングと寿命についてうそをつく必要があります。

use std::mem; 

struct HoldsVecInRefCellIter<'a> { 
    vec_ref: Ref<'a, Vec<i32>>, 
    vec_iter: Iter<'a, i32>, // 'a is a lie! 
} 

impl HoldsVecInRefCell { 
    fn iter(&self) -> HoldsVecInRefCellIter { 
     unsafe { 
      let vec_ref = self.vec_in_refcell.borrow(); 
      // transmute changes the lifetime parameter on the Iter 
      let vec_iter = mem::transmute(vec_ref.iter()); 
      HoldsVecInRefCellIter { vec_ref: vec_ref, vec_iter: vec_iter } 
     } 
    } 
} 

impl<'a> Iterator for HoldsVecInRefCellIter<'a> { 
    type Item = i32; 

    fn next(&mut self) -> Option<Self::Item> { 
     self.vec_iter.next().cloned() 
    } 
} 

この唯一の作品はIterためないRef自体に、VecRef点として、Refを移動させることによって無効化、およびIterVecのストレージではありません。

ただし、vec_iterHoldsVecInRefCellIterから移動することもできます。 vec_iterを抽出してvec_refをドロップすると、借用は解放され、Iterは無効になり、コンパイルエラー('aRefCellの有効期間です)が与えられます。適切なカプセル化を使用すると、構造体のコンテンツを非公開にして、ユーザーがこの安全でない操作を実行しないようにすることができます。

ところで、私たちは、同様の参照を返すためにイテレータを定義することができます。

impl<'a> Iterator for HoldsVecInRefCellIter<'a> { 
    type Item = &'a i32; 

    fn next(&mut self) -> Option<Self::Item> { 
     self.vec_iter.next() 
    } 
} 
関連する問題