Search code examples
iteratorrustlifetime

Returning iterator of a Vec in a RefCell


Given the following struct and impl:

use std::slice::Iter;
use std::cell::RefCell;

struct Foo {
    bar: RefCell<Vec<u32>>,
}

impl Foo {
    pub fn iter(&self) -> Iter<u32> {
        self.bar.borrow().iter()
    }
}

fn main() {}

I get an error message about a lifetime issue:

error: borrowed value does not live long enough
  --> src/main.rs:9:9
   |
9  |         self.bar.borrow().iter()
   |         ^^^^^^^^^^^^^^^^^ does not live long enough
10 |     }
   |     - temporary value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 8:36...
  --> src/main.rs:8:37
   |
8  |       pub fn iter(&self) -> Iter<u32> {
   |  _____________________________________^ starting here...
9  | |         self.bar.borrow().iter()
10 | |     }
   | |_____^ ...ending here

How am I able to return and use bars iterator?


Solution

  • You cannot do this because it would allow you to circumvent runtime checks for uniqueness violations.

    RefCell provides you a way to "defer" mutability exclusiveness checks to runtime, in exchange allowing mutation of the data it holds inside through shared references. This is done using RAII guards: you can obtain a guard object using a shared reference to RefCell, and then access the data inside RefCell using this guard object:

    &'a RefCell<T>        -> Ref<'a, T> (with borrow) or RefMut<'a, T> (with borrow_mut)
    &'b Ref<'a, T>        -> &'b T
    &'b mut RefMut<'a, T> -> &'b mut T
    

    The key point here is that 'b is different from 'a, which allows one to obtain &mut T references without having a &mut reference to the RefCell. However, these references will be linked to the guard instead and can't live longer than the guard. This is done intentionally: Ref and RefMut destructors toggle various flags inside their RefCell to force mutability checks and to force borrow() and borrow_mut() panic if these checks fail.

    The simplest thing you can do is to return a wrapper around Ref, a reference to which would implement IntoIterator:

    use std::cell::Ref;
    
    struct VecRefWrapper<'a, T: 'a> {
        r: Ref<'a, Vec<T>>
    }
    
    impl<'a, 'b: 'a, T: 'a> IntoIterator for &'b VecRefWrapper<'a, T> {
        type IntoIter = Iter<'a, T>;
        type Item = &'a T;
    
        fn into_iter(self) -> Iter<'a, T> {
            self.r.iter()
        }
    }
    

    (try it on playground)

    You can't implement IntoIterator for VecRefWrapper directly because then the internal Ref will be consumed by into_iter(), giving you essentially the same situation you're in now.