10 releases
0.1.8 | Aug 13, 2019 |
---|---|
0.1.7 | May 13, 2019 |
0.1.5 | Dec 26, 2018 |
0.1.2 | Oct 27, 2018 |
0.0.0 | Sep 30, 2018 |
#1927 in Rust patterns
32 downloads per month
Used in 2 crates
36KB
480 lines
cell
cell is a crate providing a revised RefCell
implementation with
additional mapping capabilities. It can be used as a drop-in replacement
for std::cell::RefCell
where this additional functionality is required.
Warning:
cell was found to contain a subtle unsafety, a work around for which is not known with current versions of Rust. Usage of the crate is therefore discouraged.
The Problem
A borrowed RefCell
is represented as a
Ref
. Such a Ref
contains a reference to the data
contained in the RefCell
. To extend the borrow to a different member
in the original data, the map
method can be used.
Using this method a new Ref
object can be created that contains a
reference to a member in the borrowed data.
While having a direct reference to such a data member is appropriate in many cases, there are some where this is insufficient, and an actual object that contains such a reference is required.
Example
The most prominent example is an iterator. While an iterator internally keeps a reference to the object being iterated over, it is more than just a reference to it: it contains state about the progress of the iteration.
If such an iterator is to be exposed for an object contained in a
RefCell
that is currently borrowed, the Ref::map
function is
insufficient:
struct RefStrings(RefCell<Vec<String>>);
impl RefStrings {
fn iter(&self) -> Ref<Iter<String>> {
Ref::map(self.0.borrow(), |x| x.iter())
}
}
error[E0308]: mismatched types
| Ref::map(self.0.borrow(), |x| x.iter())
| ^^^^^^^^ expected reference, found struct `std::slice::Iter`
|
= note: expected type `&_`
found type `std::slice::Iter<'_, std::string::String>`
(Note that required lifetimes have been elided in the example for brevity)
A Solution
This crate provides alternative RefCell
and Ref
implementations that
solve this problem by introduction of another mapping method: map_val
.
This method returns a RefVal
object. RefVal
is a new type that is
similar to Ref
but, instead of embedding a reference to its T
(a
type parameter), it embeds a value of it. T
in turn would contain the
actual reference to the borrowed object.
In the above example the only changes that need to happen are the
replacement of std::cell::RefCell
with cell::RefCell
, that of
std::cell::Ref
with cell::Ref
, and the usage of Ref::map
instead
of Ref::map_val
.
--- test.rs
+++ test.rs
@@ -1,13 +1,14 @@
-use std::cell::Ref;
-use std::cell::RefCell;
+use cell::Ref;
+use cell::RefCell;
+use cell::RefVal;
use std::slice::Iter;
struct RefStrings(RefCell<Vec<String>>);
impl RefStrings {
- fn iter<'t, 's: 't>(&'s self) -> Ref<'t, Iter<String>> {
- Ref::map(self.0.borrow(), |x| x.iter())
+ fn iter<'t, 's: 't>(&'s self) -> RefVal<'t, Iter<String>> {
+ Ref::map_val(self.0.borrow(), |x| x.iter())
}
}
Similar functionality exists for mutable borrow with RefValMut
.
Alternative Implementations
The possibility of providing this functionality by means of a trait has
been investigated but no viable solution has been identified. The main
problem stems from the fact that we require access to Ref
internals in
order to provide this functionality. Such a trait would alleviate the
need for providing alternative RefCell
and Ref
implementations.
No other solutions for this problem have been found, but the discussion around possible alternatives is still open as part of Rust issue #54776.