Re-interpreting a Repr-C'd struct's bytes as a certain type

Why would people even code in C anymore? Rust is the way, truth, and light brotha!!

1 Like

If you haven't already, please read the Rust nomicon before writing any more unsafe code.

1 Like

I've always enjoyed occultism anyways... there's indeed some crazy things in this universe. Oh, and on the side if you're curious, check out this book I wrote. https://thomaspbraun.com/TheBridge.pdf

It actually got academic recognition this past April, despite it being quite... well, you'll see

2 Likes

Note that while you can get &mut T from &UnsafeCell<T>, converting &UnsafeCell<T> into &mut UnsafeCell<T> still is UB.

5 Likes

This comment made me feel unsafe...

1 Like

Not... that you could do very much with a &mut UnsafeCell<T> that you couldn't do with a &UnsafeCell<T>, other than the following:

let x: &UnsafeCell<T> = /**/;
*x = UnsafeCell::new(/**/);

But there would be no point to that because then you'd just

*x.get() = /**/;
1 Like

True, but I think he was pointing it out to just get the idea across of the meta-ness of how the compiler may work

1 Like

Right I understand.

But in addressing dynamics you wouldn't want to skip the additional checks?

Hence it's notorious overhead?

Have you heard the infinitely dense space theory in physics?

I 100% believe the guy. Brilliant! A genius.

"This comment made me feel unsafe..."

I am supposed to feel unsafe rather than be unsafe? I thought I was supposed to feel transient anxiety.

1 Like

Oh no, you are thinking about this wrong, without UnsafeCell the compiler will assume that writing to something that is transitively behing a shared reference is impossible, and will optimize based on that assumption. If you do write to something behind an shared reference, unless it is wrapped in a UnsafeCell, the compiler is free to throw out that write completely. This is not what you wanted, so to remove these optimizations, you must use UnsafeCell.

1 Like

That sounds awesome. Can you send me a link? I'm getting, as expected, lots of results for theories regarding black holes via Google

In release mode, or debug? In debug, I am able to write via a shared ref no problem.

In both modes. That it works is a coincidence, and the same code may suddenly start failing in the future if some optimization is added to llvm or rust.

2 Likes

Uhhhhh, guys?

You don't need an UnsafeCell if you have a *const T or *mut T.


Wait a second, I misread the code I linked. What the heck!?!?!

unsafe impl<T: ?Sized> Freeze for *const T {}
unsafe impl<T: ?Sized> Freeze for *mut T {}

That... that can't be right!

@cuviper how did you construct those playground tests before? I'm having a difficult time getting the functions to not be eliminated as dead code. (I wish the playground had a --lib mode...).
(edit: never mind, I finally spotted the playground link in your post)

I may need to read up on LLVM's noalias. Perhaps it only extends to data accessed through an offset of the pointer itself, and not to data accessed through another layer of indirection.

2 Likes

UnsafeCell does not have this function

I meant UnsafeCell::get.


Okay, here's what happens when UnsafeCell appears behind multiple layers of indirection:

pub fn f(_: &&Cell<i32>) {} // i32** noalias readonly align 8 dereferenceable(8)
pub fn f(_: &*mut i32) {}   // i32** noalias readonly align 8 dereferenceable(8)
pub fn f(_: &i32) {}        // i32* noalias readonly align 4 dereferenceable(4)

pub fn f(_: &Cell<i32>) {} // i32* align 4 dereferenceable(4)
pub fn f(_: *mut i32) {}   // i32*
pub fn f(_: *const i32) {} // i32*

Notice how &&Cell has noalias readonly, just like &*mut i32. Hence I stand by what I said: You only need UnsafeCell<T> if you have a T stored by value in rust code. You do not need it if all you have is a raw pointer to a T.

Yeah, that's what I thought. I didn't know what was happening through the lens of LLVM though. In this struct's case, there is no need for T except when the user casts to it

The pointer itself implements Freeze, but this says nothing about T.

/// Compiler-internal trait used to determine whether a type contains
/// any `UnsafeCell` internally, but not through an indirection.

Yes, that's how I understand it.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.