Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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>.
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading