Alternate Solution
Here is an alternate solution that uses interior mutability as it was intended. Instead of creating an iterator for &T
values, we should create an iterator for Ref<T>
values, which deference automatically.
struct Iter<'a, T> {
inner: Option<Ref<'a, [T]>>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = Ref<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
match self.inner.take() {
Some(borrow) => match *borrow {
[] => None,
[_, ..] => {
let (head, tail) = Ref::map_split(borrow, |slice| {
(&slice[0], &slice[1..])
});
self.inner.replace(tail);
Some(head)
}
},
None => None,
}
}
}
Playground
Explanation
The accepted answer has a few significant drawbacks that may confuse those new to Rust. I will explain how, in my personal experience, the accepted answer might actually be harmful to a beginner, and why I believe this alternative uses interior mutability and iterators as they were intended.
As the previous answer importantly highlights, using RefCell
creates a divergent type hierarchy that isolates mutable and immutable access to a shared value, but you do not have to worry about lifetimes to solve the iteration problem:
RefCell<T> .borrow() -> Ref<T> .deref() -> &T
RefCell<T> .borrow_mut() -> RefMut<T> .deref_mut() -> &mut T
The key to solving this without lifetimes is the Ref::map
method, which is critically missed in the book. Ref::map
"makes a new reference to a component of the borrowed data", or in other words converts a Ref<T>
of the outer type to a Ref<U>
of some inner value:
Ref::map(Ref<T>, ...) -> Ref<U>
Ref::map
and its counterpart RefMut::map
are the real stars of the interior mutability pattern, not borrow()
and borrow_mut()
.
Why? Because unlike borrow()
and borrow_mut()
, Ref::mut
and RefMut::map
, allow you to create references to interior values that can be "returned".
Consider adding a first()
method to the Foo
struct described in the question:
fn first(&self) -> &u32 {
&self.bar.borrow()[0]
}
Nope, .borrow()
makes a temporary Ref
that only lives until the method returns:
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:9:11
|
9 | &self.bar.borrow()[0]
| ^-----------------^^^
| ||
| |temporary value created here
| returns a value referencing data owned by the current function
error: aborting due to previous error; 1 warning emitted
We can make it more obvious what is happening if we break it up and make the implicit deference explicit:
fn first(&self) -> &u32 {
let borrow: Ref<_> = self.bar.borrow();
let bar: &Vec<u32> = borrow.deref();
&bar[0]
}
Now we can see that .borrow()
creates a Ref<T>
that is owned by the method's scope, and isn't returned and therefore dropped even before the reference it provided can be used. So, what we really need is to return an owned type instead of a reference. We want to return a Ref<T>
, as it implements Deref
for us!
Ref::map
will help us do just that for component (internal) values:
fn first(&self) -> Ref<u32> {
Ref::map(self.bar.borrow(), |bar| &bar[0])
}
Of course, the .deref()
will still happen automatically, and Ref<u32>
will be mostly be referentially transparent as &u32
.
Gotcha. One easy mistake to make when using Ref::map
is to try to create an owned value in the closure, which is not possible as when we tried to use borrow()
. Consider the type signature of the second parameter, the function: FnOnce(&T) -> &U,
. It returns a reference, not an owned type!
This is why we use a slice in the answer &v[..]
instead of trying to use the vector's .iter()
method, which returns an owned std::slice::Iter<'a, T>
. Slices are a reference type.
Additional Thoughts
Alright, so now I will attempt to justify why this solution is better than the accepted answer.
First, the use of IntoIterator
is inconsistent with the Rust standard library, and arguably the purpose and intent of the trait. The trait method consumes self
: fn into_iter(self) -> ...
.
let v = vec![1,2,3,4];
let i = v.into_iter();
// v is no longer valid, it was moved into the iterator
Using IntoIterator
indirectly for a wrapper is inconsistent as you consume the wrapper and not the collection. In my experience, beginners will benefit from sticking with the conventions. We should use a regular Iterator
.
Next, the IntoIterator
trait is implemented for the reference &VecRefWrapper
and not the owned type VecRefWrapper
.
Suppose you are implementing a library. The consumers of your API will have to seemingly arbitrarily decorate owned values with reference operators, as is demonstrated in the example on the playground:
for &i in &foo.iter() {
println!("{}", i);
}
This is a subtle and confusing distinction if you are new to Rust. Why do we have to take a reference to the value when it is anonymously owned by - and should only exist for - the scope of the loop?
Finally, the solution above shows how it is possible to drill all they way into your data with interior mutability, and makes the path forward for implementing a mutable iterator clear as well. Use RefMut
.