Potential danger of Mutf32 (Refcell<f32> alternative)

Based on feedback

What if I rewrote it as follows:

pub struct MutPrim<T> where
    T: Copy
{
    v: T,
}

impl<T> MutPrim<T> {
    fn get_mut_ptr(&self) -> *mut T {
        &self.v as *const T as *mut T
    }

    pub fn set(&self, f: T) {
        let ptr = &self.v as *const T as *mut T;
        unsafe { *ptr = f; }
    }

    pub fn get(&self) -> T {
        &self.v
    }
}


type MutF32 = MutPrim<f32>;

Orig

Hi,

I know about Refcell for interior mutability. However, I'm not a fan of the Refcell api, and was wondering, for the case of f32, if it is safe to do the following:

pub struct Mutf32 {
    v: f32,
}

impl Mutf32 {
    pub fn get_mut_ptr(&self) -> *mut f32 {
        unsafe {
            &self.v as *const f32 as *mut f32
        }
    }
}

Here are the arguments / counter arguments I would like help sorting out:

Pessimist: This is unsafe. You are using the word 'unsafe'
Optimist: This should work. Although it's unsafe to cast a const* f32 to a *mut f32, the location of the address stays the same. Thus, any references should remain valid.

Pessimist: Well, the value can change behind the programmer's back, this is bad if the programmer is expecting the value to not change.
Optimist: This is not a problem. The struct is claled Mutf32, if the programmer expects it to stay const, it's programmer error.

Pessimist: What if the compiler expects it to stay constant and does compiler optimizations ...
Optimist: .... I'm not sure ...

This, my questions:

What are the dangers I should be aware of when using the above in my code?

The unsafe block in get_mut_ptr isn't necessary - creating raw pointers is safe. Dereferencing them is unsafe, since that's the point at which you need to ensure the address is still valid, there aren't any outstanding borrows if you're writing to it, etc.

It looks like you just discovered Cell. :wink:

But you're missing a key element that makes Cell safe: it's implemented using UnsafeCell. UnsafeCell is what tells the compiler "this value can be mutated through a & reference". Otherwise, compiler optimizations really can mess you up bad.

2 Likes

What if I rewrite it as:

pub struct MutPrim<T> where
    T: Copy
{
    v: T,
}

impl<T> MutPrim<T> {
    fn get_mut_ptr(&self) -> *mut T {
        &self.v as *const T as *mut T
    }

    pub fn set(&self, f: T) {
        let ptr = &self.v as *const T as *mut T;
        unsafe { *ptr = f; }
    }

    pub fn get(&self) -> T {
        &self.v
    }
}


type MutF32 = MutPrim<f32>;

MutPrim is an unsound version of UnsafeCell.

1 Like

I read the source for Cell and skimmed the source for UnsafeCell.

Cell indeed is what I want.

Thanks everyone for insights / helping me work through / explaining flaws in my attempts!