I really like the Ref2Φ
idea. It preserves the property that once borrowed, an object cannot be mutated, which I like. Moreover, it seems to capture exactly the sort of borrowing behavior I wanted from streaming iterators:
trait StreamingIterator {
type Item<'a>;
fn next<'immut, /* N.B.: no #[may_dangle] */ 'mubl>(
self: Ref2Φ<'immut, 'mutbl, Self>
) -> Item<'immut>;
}
This would allow, e.g., doing a nice iteration through a file while still being able to access the file properties (path, size, etc.) in the middle of iteration.
However, instead of using “not-yet-active” lifetimes, it might be cleaner to just use arbitrary sets of program points (not necessarily contiguous) for the mutable section. With a lifetime, a reference would be able to be changed from immutable to mutable to immutable, but not anything more complex than that. This seems like an odd choice, and it lacks symmetry - labeling the immutable part of the borrow would allow a completely different set of patterns.
Essentially, your initial Ref2Φ
proposal allows
let v_ref = &mut v;
Vec::push(v_ref, Vec::len(&v));
and
let v_ref = &mut v;
Vec::push(v_ref, 3);
Vec::push(v_ref, 5);
but disallows
let v_ref = &mut v;
Vec::push(v_ref, Vec::len(&v));
Vec::push(v_ref, 5);
which seems strange to me.
Another way to look at this is as a generalized notion of the mutability part of a reference. A reference currently has three parts - a lifetime, a mutability (mut
vs. not), and a type. This would just change the mutability part from a binary choice to a binary choice at every program point. Therefore, a change like this could be initially exposed to the user by just being generic over mutability (which has already been RFCed).