Immutable Frozen<T> type

Well; strictly speaking it can become part of the type:

#![feature(optin_builtin_traits)]

mod frozen {
    use core::{ops::Deref, cell::UnsafeCell, marker::PhantomData};

    pub unsafe auto trait Interiorless {}
    impl<T: ?Sized> !Interiorless for UnsafeCell<T> {}
    unsafe impl<T: ?Sized> Interiorless for PhantomData<T> {}
    unsafe impl<T: ?Sized> Interiorless for *const T {}
    unsafe impl<T: ?Sized> Interiorless for *mut T {}
    unsafe impl<T: ?Sized> Interiorless for &T {}
    unsafe impl<T: ?Sized> Interiorless for &mut T {}

    pub struct Frozen<T> {
        value: T,
    }

    impl<T> Frozen<T> {
        pub fn new(value: T) -> Self {
            Self { value }
        }
    }

    impl<T: Interiorless> Deref for Frozen<T> {
        type Target = T;

        fn deref(&self) -> &T {
            &self.value
        }
    }
}

The logic of this is that while you may have let mut x = Frozen::new(1);, there are no &mut operations on Frozen<u8> so it is immutable; furthermore, with T: Interiorless interior mutability is forbidden so that avenue is closed as well. What remains are Clone, Copy, and other such "I can recreate T from &T facilities" but that's fine under "immutable".

The usefulness of Frozen is debatable tho.

4 Likes

@Centril
You're code for Frozen is broken,

let cell = Cell::new(10);
let f = Frozen::new(&cell);
f.set(20);
println!("{:?}", f.get());

Will compile and run just fine under the current definition.
These two are what break it in Safe Rust.

unsafe impl<T: ?Sized> Interiorless for &T {}
unsafe impl<T: ?Sized> Interiorless for &mut T {}

It's not really broken so much as it is different. What x: Frozen<T> says is that "the memory owned by x may not be mutated through x even if x is a mutable binding."

Indeed. This is expected. The notion of Interiorless here is copied from the compiler's code where it is called Freeze. If you want to stronger notion of "no memory reachable through x: Frozen<T> may be mutated" you can instead change:

    unsafe impl<T: ?Sized> Interiorless for &T {}
    unsafe impl<T: ?Sized> Interiorless for &mut T {}

into:

    unsafe impl<T: ?Sized + Interiorless> Interiorless for &T {}
    unsafe impl<T: ?Sized + Interiorless> Interiorless for &mut T {}

and now you cannot have Frozen<&mut? Cell<T>>.

1 Like

Ah, I misunderstood that, that makes sense. Thanks!

1 Like