Motivation
Currently, because Rust has two distinct reference types, a common pattern exists, where many types have get
and get_mut
accessor functions, which most of the time are exact copies of each other, only differing in mutability. This is some unwanted code-duplication, which this is supposed to address.
Unified References
Instead of being two separate types, both types of references could be variants of one generic reference type that is generic over mutability.
Conceptually (not real Rust code), this could look something like:
struct Reference<'t, const MUTABILITY: bool, T> {/* ... */}
type &'t T = Reference<'t, false, T>;
type &'t mut T = Reference<'t, true, T>;
No Duplicate Code Generation
Preferebly, mutability parameters would be similar to lifetimes in the way that the compiler guarantees that it won't emit two different functions for calls only differing in mutability.
Misusing const M: bool
like above would therefore be problematic. This would allow for implementing different behavior depending on mutability inside a function that is generic over mutability, so we will have to introduce a new type of generic argument for mutability.
Conceptually (still not real Rust code), this could look something like:
struct Reference<'t, mut M, T> {/* ... */}
type &'t T = Reference<'t, const, T>;
type &'t mut T = Reference<'t, mut, T>;
Proposed Syntax
To do anything useful with generic unified references, we would need some accompanying syntax for taking borrows generic on mutability. It comes natural to have this syntax analogous to &mut foo
:
struct T(u32);
impl T {
fn get<mut M>(&M self) -> &M u32 {
&M self.0
}
}
Borrowing Rules
Used generically, a reference would have the restrictions of both &mut T
and &T
:
let t = T::new();
let t: &M T = &M t;
// Not Copy/Clone, because it might be a unique reference:
// let t = *&t; // Error
// Not writable of course because it might be shared reference
// *t = T::new(); // Error
// Cannot be mutably borrowed, because it might be shared
// let _: &mut T = &mut *t; // Error
// It can however be generically borrowed:
let a = &M T = &M *t;
// But only one active generic borrow, similar to mutable borrows:
// let b = &M T = &M *t; // Error
a.foo();
// And of course you can have multiple shared borrows active:
let a = &*t;
let b = &*t;
a.foo();
Misc
Another use could be an iterator that is generic over mutability:
struct MyIterator<'t, mut M, T> {
/* */
}
impl<'t, mut M, T> Iterator for FooIterator<'t, M, T> {
type Item = &'t M T;
/* ... */
}