Understanding unsafe code and is it idiomatic?

Every once in awhile I use cargo-geiger to check which of my most commonly used dependencies contain unsafe blocks and then look into how they are used.
Most of the time these are short code blocks that one can follow easily after spending some time with the surrounding code.

When looking into serde_bytes I encountered the following unsafe block:

unsafe { &mut *(&mut self.bytes as &mut [u8] as *mut [u8] as *mut Bytes) }

I find it really hard to quickly understand such code and, therefore, reason about the safety of code like this. At first, it just looks like a combination of all permutations of slices, (de)references and mutability.
I am sure this code makes perfect sense the way it is.

My question is more like would there be any easier or more accessible way to express the same functionality? Is this the most idiomatic way?

This is not about criticizing a particular crate, others also contain hard to follow unsafe blocks, it is more about my (and hopefully also others) slow brain that needs longer to review/parse code blocks like this :wink:

Thanks for any insight and have a nice day!

1 Like

I sometimes read (and wrote) such codes, but I don't know more idiomatic code than your example.

In old days, some people were using std::mem::transmute() to convert references, for example mem::transmute::<_, &mut Bytes>(&mut self.bytes).
It is easy to read, however std::mem::transmute() is considered too powerful for this purpose.

Now clippy has transmute_ptr_to_ptr lint to discourage uses of transmute() for this purpose, and unsafe { &*(ref_t as *const T as *const U) } style is recommended instead.

P.S.
std::str::from_utf8_unchecked_mut is an example of doing such reference conversion, and it also uses as rather than transmute().
I think conversions in this way are widely accepted and defacto standard.

Quick reasoning is not something you want someone reviewing unsafe code to do.
You need to know what types are being handled which means reading further away from just the line containing the block.

Such lines can be split over a few assignments. Down to reader to decide if they prefer such code.

The cast function has more recently been added. (Note there isn't an exact suffix ptr->ref so optimal code can't be read from left to right.)

unsafe { <*mut [u8]>::cast::<*mut Bytes>(self.bytes.as_mut_slice()).as_mut().unwrap() }
1 Like