[Idea] Immutable and exclusive reference
Rust has two types of references, &T which is immutable and shareable, and &mut T which is mutable but exclusive. The borrow checker ensures that there is always only one type of reference to a type and thus ensures memory safety.
Logically there could be two more reference types:
- mutable and shareable
- immutable and exclusive
Option 1 is obviously completely unsafe and will never be introduced in Rust. Option 2 (let's call it "&fixed T" for now) on the other hand is very restricted and safe. But there is no obvious use case for it, since it is strictly less powerful than both &T and &mut T. *
Casting &mut to &fixed (and back!)
But I have an idea which could provide a use case and allow code that AFAIK is not currently possible in safe Rust:
- Allow converting a &mut T into an arbitrary, but fixed number of &fixed T, e.g.:
let m = &mut x; let ref fixed (a, b, c) = m;
- These &fixed T references can then be passed around separately and reborrowed just as a &mut T, but their number cannot be increased. They also cannot coexist with a &mut T or &T reference.
- If you collect all of the &fixed T references you can convert them back into an &mut T:
let ref mut m = (a, b, c);
(Syntax as well as the name &fixed is obviously subject to bikeshedding.)
Since the number of &fixed T references is fixed at creation, the borrow checker can keep track of them and only allow merging them into &mut if all of them are accounted for.
In effect this allows fragmenting and later re-assembling a mutable reference.
Is this safe?
I will yield to the experts here, but I don't see why it should not be. At any point in time there is only ever a mutable, exclusive reference or a fixed number of immutable, (non-shareable) ones.
The difficulty and innovation(?) here is obviously at step 3. This is not in general possible for shared references &T because they can be copied and the borrow checker would need to prove that no more shared references exist anywhere else.
With &fixed references this is much easier, since their number can never increase after their creation. The borrow checker can annotate each &fixed reference with the total number that exists and only allow re-assembly of the &mut if all of the existing &fixed references are collected (or dead).
Is this feasible?
I'm not a compiler writer so I don't know for sure. Exclusive and immutable references used to exist in pre-1.0 Rust (see footnote) so I don't expect this to be a big problem. Allowing the re-assembly of &mut from &fixed might be more difficult, but seems possible. Depending on the syntax there might need to be a new reserved keyword.
Is this useful?
Suppose you have a &mut T reference to a node in a data structure (say a directed graph of some kind). You want to traverse the graph in several directions at once while keeping a reference to your original node, so you can compare them. So you fragment the &mut reference into several &fixed references which you can take along on the traversals. At some point you want to collect all the data gathered from the traversals and use it to update the original node. Now you can recreate the &mut reference from the &fixed references. Doing the same thing currently would be much more difficult. Now, admittedly this a very constructed scenario. But then again I don't really work on graph theory and if I can come up with an example, then I'm sure people could find many creative uses for this.
In theory &T references and &fixed T references could co-exist, because they are all immutable. However when we want to reconstruct a &mut T from the &fixed T references, no &T can still be alive. This might be difficult to prove.
Should &fixed T references be constructible directly from an owned type? Probably. They will only be useful though if you want to assemble a &mut T later. They can not be cast from &T, because that would indirectly allow casting &T to &mut T.
I'm not that happy with the name &fixed (or &fix?). Any better suggestions?
I thought of this while reading this discussion on the user forum. I have not been using Rust for very long and don't have that much experience with it. I mostly expect that this idea will be shot down as infeasible or not useful enough, but I still wanted to post it in case it does have some merit.
*It seems to actually have existed in pre- 1.0 Rust under the name &const T and was removed due to the perceived uselessness. However the name &const T is confusing with the usual meaning of const in Rust and should probably not be reused, if this idea goes anywhere.