Design a trait in Rust with a mutable getter and an immutable getter and that implements the immutable getter by default

Advertisements

I want to design a trait with a mutable and an immutable getter to some field of self.
However, I want that the implementer of the trait has to implement only one method, typically the mutable getter.

I have found this solution (playground):

pub trait MyTrait<T> {
    fn inside_mut(&mut self) -> &mut T; 
    
    fn inside_ref(&self) -> &T {
        let ptr = self as *const Self as *mut Self;
        &* unsafe { &mut *ptr }.inside_mut()
    }
}

pub struct Data(String);
impl MyTrait<String> for Data {
    fn inside_mut(&mut self) -> &mut String { &mut self.0 }
}

fn main() {
    let mut data = Data(format!("Foo"));
    let x: &String = data.inside_ref();
    let y: &String = data.inside_ref();
    println!("x -> {x}");
    println!("y -> {y}");
    *data.inside_mut() = format!("Bar");
    println!("data.0 -> {}", data.0);
}

Result:

Standard Error

   Compiling playground v0.0.1 (/playground)
    Finished release [optimized] target(s) in 0.93s
     Running `target/release/playground`

Standard Output

x -> Foo
y -> Foo
data.0 -> Bar

My question: is this approach sound?

>Solution :

No, this is not sound.

The user could modify self in his implementation of inside_mut, which would also modify the struct in inside_ref.

Like this:

pub trait MyTrait<T> {
    fn inside_mut(&mut self) -> &mut T;

    fn inside_ref(&self) -> &T {
        let ptr = self as *const Self as *mut Self;
        &*unsafe { (&mut *ptr).inside_mut() }
    }
}

#[derive(Debug)]
struct MyStruct {
    value: i32,
}

impl MyTrait<i32> for MyStruct {
    fn inside_mut(&mut self) -> &mut i32 {
        self.value += 1;
        &mut self.value
    }
}

fn main() {
    // Important: this is not `mut`!!!
    let x = MyStruct { value: 42 };

    println!("{:?}", x);
    x.inside_ref();
    println!("{:?}", x);
}
MyStruct { value: 42 }
MyStruct { value: 43 }

That means that this implementation is unsound:

Behavior considered undefined

  • Mutating immutable data. All data inside a const item is immutable. Moreover, all data reached through a shared reference or data owned by an immutable binding is immutable, unless that data is contained within an UnsafeCell<U>.

Leave a ReplyCancel reply