简体   繁体   English

是否有用于 Rc 的迭代器的优雅实现<refcell<t> &gt; 在 Rust 中? </refcell<t>

[英]Is there a elegant implement of the Iterator for the Rc<RefCell<T>> in Rust?

When I tried to implement the SkipList by rust, I was trapped by the implement of Iterator of Rc<RefCell<T>> .当我试图通过 rust 实现 SkipList 时,我被Rc<RefCell<T>>Iterator的实现所困。 Code is listed:代码如下:

pub struct SkipList<K, V>
{
    head: Rc<RefCell<SkipNode<K, V>>>,
    rng: rand::rngs::ThreadRng,
    len: usize,
}
impl<K: Ord, V> SkipList<K, V> {
// ...
    pub fn iter(&self) -> Iter<K, V> {
        let next = &RefCell::borrow(&self.head).next_by_height[0];
        Iter {
            ptr: next.as_ref().map(|ref cell|Rc::clone(cell)),
            _marker: Default::default(),
        }
    }
}
struct SkipNode<K, V> {
    entry: Entry<K, V>,
    next_by_height: [Option<Rc<RefCell<SkipNode>>>; MAX_HEIGHT],
}
pub struct Entry<K, V> {
    key: K,
    value: V,
}

struct SkipNode<K, V> {
    entry: Option<Entry<K, V>>,
    next_by_height: SkipTrack<K, V>,
}

pub struct Iter<'a, K: Ord, V>
{
    ptr: Option<Rc<RefCell<SkipNode<K, V>>>>, //1
    _marker: marker::PhantomData<&'a K>,
}

impl<'a, K, V: 'a> Iterator for Iter<'a, K, V>
where K: Ord
{
    type Item = Ref<'a, Entry<K, V>>; //2

    fn next(&mut self) -> Option<Self::Item> {
        self.ptr.take().map(|node|  { //3
            let current = RefCell::borrow(&node);
            self.ptr = current.next_by_height[0].as_ref().map(|ref node| Rc::clone(node));
            Ref::map(current, |ref wrapped|{
              &wrapped.entry.unwrap()
            })
        })
    }
}

and the error is:错误是:

   Compiling RclessRefCelllessTgreatergreater-Rust v0.1.0 (/home/runner/RclessRefCelllessTgreatergreater-Rust)
error[E0515]: cannot return reference to temporary value
   --> main.rs:138:15
    |
138 |               &wrapped.entry.unwrap()
    |               ^----------------------
    |               ||
    |               |temporary value created here
    |               returns a reference to data owned by the current function

error[E0507]: cannot move out of `wrapped.entry` which is behind a shared reference
   --> main.rs:138:16
    |
138 |               &wrapped.entry.unwrap()
    |                ^^^^^^^^^^^^^
    |                |
    |                move occurs because `wrapped.entry` has type `std::option::Option<Entry<K, V>>`, which does not implement the `Copy` trait
    |                help: consider borrowing the `Option`'s content: `wrapped.entry.as_ref()`

error[E0515]: cannot return value referencing function parameter `node`
   --> main.rs:137:13
    |
135 |               let current = RefCell::borrow(&node);
    |                                             ----- `node` is borrowed here
136 |               self.ptr = current.next_by_height[0].as_ref().map(|ref node| Rc::clone(node));
137 | /             Ref::map(current, |ref wrapped|{
138 | |               &wrapped.entry.unwrap()
139 | |             })
    | |______________^ returns a value referencing data owned by the current function

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0507, E0515.
For more information about an error, try `rustc --explain E0507`.
error: could not compile `RclessRefCelllessTgreatergreater-Rust`.

the full code is avaliable at Repl.it .完整代码可在Repl.it 获得

I attempted to take the Ref<T> as the Item returned by Iterator , but the code is complained by the Rust complier that next could not return a temporary variable.我试图将Ref<T>作为Iterator返回的 Item ,但是 Rust 编译器抱怨代码next无法返回临时变量。 Is there any Silver Bullet to achieve the Iterator for the wrapped cell?是否有任何灵丹妙药来实现包装单元的Iterator

Is there a elegant implement of the Iterator for the Rc<RefCell> in Rust? Rust 中的 Rc<RefCell> 的迭代器是否有优雅的实现?

Not really.并不真地。 There's two big things in your way.你的路上有两件大事。


One limitation when building an Iterator is that the output type cannot reference the iterator itself.构建Iterator时的一个限制是 output 类型不能引用迭代器本身。 It can either return an owned value (no lifetimes involved) or return references bound to the lifetime of some other object.它可以返回一个拥有的值(不涉及生命周期)或返回绑定到其他一些 object 的生命周期的引用。

I see you've tried to communicate that you want to return Ref s bound to the lifetime of the orignal SkipList by using PhantomData<&'a K> and also using 'a in your Iterator::Item .我看到您已尝试通过使用PhantomData<&'a K>并在Iterator::Item中使用'a来传达您希望返回绑定到原始SkipList生命周期的Ref的信息。 However, since your iterator can only source values from ptr , an owned value with no lifetime-linkage to 'a , the compiler will complain about mismatched lifetimes at one point or another.但是,由于您的迭代器只能从ptr获取值,这是一个与'a没有生命周期链接的拥有值,因此编译器会在某一时刻抱怨生命周期不匹配。

In order to do this, your iterator would have to use something bound to 'a , probably some form of Option<Ref<'a, _>> .为了做到这一点,你的迭代器必须使用绑定到'a东西,可能是某种形式的Option<Ref<'a, _>>


However, another limitation is when you have nested RefCell s, with each level that you iterate, you need to keep around an additional Ref .但是,另一个限制是当您嵌套RefCell时,对于您迭代的每个级别,您都需要保留一个额外的Ref The reason being that borrows from a Ref don't keep the lifetime of the RefCell , but from the Ref itself.原因是从Ref借用的不是保持RefCell的生命周期,而是从Ref本身。

let refc = RefCell::new(RefCell::new(RefCell::new(42)));
let ref1 = refc.borrow(); // these all need
let ref2 = ref1.borrow(); // to be preserved
let ref3 = ref2.borrow(); // to access `v`
let v = &*ref3;

So if you try to keep only one Ref chained from another, its going to encounter a "returns a value referencing data owned by the current function" error in one form or another.因此,如果您尝试仅将一个Ref与另一个链接起来,它将以一种或另一种形式遇到“返回引用当前函数拥有的数据的值”错误。 If you instead try to keep all those Ref s within the iterator, well then it creates a self-referential struct.如果您尝试将所有这些Ref保留在迭代器中,那么它会创建一个自引用结构。 Not nice.不太好。

You can only get out of this issue by cloning the Rc s at each level (which you've already done) to to get an owned value and escape the lifetime of the previous Ref .您只能通过在每个级别(您已经完成)克隆Rc来摆脱这个问题,以获得拥有的价值并逃避前一个Ref的生命周期。


So you cannot return a reference type for your iterator.所以你不能为你的迭代器返回一个引用类型。 Your only option is to return an owned value, which in this case wouldn't be ideal since it'd either have to be a cloned Entry , or an Rc<RefCell<SkipNode<K, V>>> , or a wrapper around an Rc that exposes the Entry properly.您唯一的选择是返回一个拥有的值,在这种情况下这并不理想,因为它必须是一个克隆Entry ,或者一个Rc<RefCell<SkipNode<K, V>>> ,或者一个包装器正确公开EntryRc

Here's a working version.这是一个工作版本。 The EntryRef wrapper I added could probably be better but it works and demonstrates the point.我添加的EntryRef包装器可能会更好,但它可以工作并证明了这一点。

pub struct EntryRef<K, V> {
    ptr: Rc<RefCell<SkipNode<K, V>>>,
}

impl<K, V> EntryRef<K, V> {
    pub fn get_entry(&self) -> Ref<'_, Entry<K, V>> {
        Ref::map(self.ptr.borrow(), |node| node.entry.as_ref().unwrap())
    }
}

pub struct Iter<K: Ord, V> {
    ptr: Option<Rc<RefCell<SkipNode<K, V>>>>,
}

impl<K, V> Iterator for Iter<K, V>
where
    K: Ord,
{
    type Item = EntryRef<K, V>;

    fn next(&mut self) -> Option<Self::Item> {
        self.ptr.take().map(|node| {
            self.ptr = node.borrow().next_by_height[0]
                .as_ref()
                .map(|ref node| Rc::clone(node));
            EntryRef { ptr: node }
        })
    }
}

See also:也可以看看:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM