@SkiFire13
I've created a small demo with (almost) safe rust, and I have no idea how the interior mutability could affect the code:
use core::ops::{Deref, DerefMut};
use core::marker::PhantomData;
pub struct Avatar<'a,'b:'a, T:?Sized>(&'a mut T, PhantomData<&'b T>);
impl<'a,'b:'a,T:?Sized> Avatar<'a,'b,T> {
pub fn from(t:&'b mut T)->Self{
Self(t, PhantomData)
}
pub fn immut(self)->&'b T {
// SAFETY: the original borrow lives at least `&'b mut`
// which allows such transmute directly
unsafe {core::mem::transmute::<&'a mut T, &'b T>(self.0)}
// since currently we always borrow `t` for at least &mut 'b
// we could directly store the (&'b mut T) rather than storing `'a` and extending it here.
}
}
impl<'a,'b:'a,T:?Sized> Deref for Avatar<'a,'b,T> {
type Target = T;
fn deref(&self)->&T {
&self.0
}
}
impl<'a,'b:'a,T:?Sized> DerefMut for Avatar<'a,'b,T> {
fn deref_mut(&mut self)->&mut T {
&mut self.0
}
}
pub struct RAW(i32);
impl RAW {
pub fn read(&self)->&i32{&self.0}
pub fn pointer(&mut self)->&mut i32{&mut self.0}
pub fn write(&mut self, data:i32){*self.pointer()=data}
}
impl<'a,'b:'a> Avatar<'a,'b,RAW> {
// Due to current limitations, we cannot tell compiler the originally borrowed item is
// borrowable now, thus we return its references.
pub fn read_after_write(mut self, data:i32)->(&'b i32,&'b RAW) {
self.write(data);
let immut = self.immut();
(immut.read(), immut)
}
}
The avatar plays the role of a BorrowHybrid
, with (almost) safe Rust. If you're worrying about safety, here is a totally safe version with relaxed lifetime markers(it is &'b mut
rather than a shorter &'a mut
is stored):
use core::ops::{Deref, DerefMut};
use core::marker::PhantomData;
pub struct Avatar<'a,'b:'a, T:?Sized>(&'b mut T, PhantomData<&'a T>); // In safe version, 'a is a
// useless lifetime.
impl<'a,'b:'a,T:?Sized> Avatar<'a,'b,T> {
pub fn from(t:&'b mut T)->Self{
Self(t, PhantomData)
}
pub fn immut(self)->&'b T {
self.0
}
}
impl<'a,'b:'a,T:?Sized> Deref for Avatar<'a,'b,T> {
type Target = T;
fn deref(&self)->&T {
&self.0
}
}
impl<'a,'b:'a,T:?Sized> DerefMut for Avatar<'a,'b,T> {
fn deref_mut(&mut self)->&mut T {
&mut self.0
}
}
Here is a demo, since currently Rustc does not allow return a shared reference back to variable borrowed exclusively then allow shared borrows of this struct, we do such thing manually, returning a shared references together with the wanted read-after-write references.
pub struct RAW(i32);
impl RAW {
pub fn read(&self)->&i32{&self.0}
pub fn pointer(&mut self)->&mut i32{&mut self.0}
pub fn write(&mut self, data:i32){*self.pointer()=data}
pub fn read_after_write(&mut self, data:i32)->&i32 { // original version.
self.write(data);
// now self is read-only
self.read()
}
}
impl<'a,'b:'a> Avatar<'a,'b,RAW> {
// Due to current limitations, we cannot tell compiler the originally borrowed item is
// borrowable now, thus we return its references.
pub fn read_after_write(mut self, data:i32)->(&'b i32,&'b RAW) {
self.write(data);
// now self is read-only
let immut = self.immut();
// return both read-after-write references and a references of `RAW` struct.
(immut.read(), immut)
}
}
fn main(){
let mut a = RAW(0);
println!("{}", a.read());
a.write(1);
println!("{}", a.read());
*a.pointer()=2;
println!("{}", a.read());
{
let mut a = Avatar::from(&mut a);
println!("{}", a.read());
a.write(3);
println!("{}", a.read());
*a.pointer()=4;
println!("{}", a.read());
let (read,a) = a.read_after_write(5);
// Due to the limitation of current compiler, I have no idea how to tell compiler that,
// `a` *is currently borrowable*. Thus I have to send this borrow back.
println!("{} {}", a.read(),read);
}
println!("{}", a.read());
}