Lokathor / bytemuck

A crate for mucking around with piles of bytes
https://docs.rs/bytemuck
Apache License 2.0
725 stars 79 forks source link

Raw pointer cast functions #202

Open bravely-beep opened 1 year ago

bravely-beep commented 1 year ago

Would it make sense to include cast functions that handle raw pointers?

This would be useful when working with data that is unsafe to take references to (e.g. volatile IO memory).

zachs18 commented 1 year ago

I would not necessarily be opposed to these, but a few notes:

  1. (I assume their failure conditions and semantics would be mostly the same as cast_ref/cast_mut/cast_slice/cast_slice_mut? Otherwise I don't really see the point; if they are meant to always succeed you can just as-cast them.)
  2. As has been mentioned in other issues/PRs, bytemuck already has a lot of functions (https://github.com/Lokathor/bytemuck/pull/132#issuecomment-1234896922)
  3. If these were added, I would expect the corresponding try_ functions to also exist.
  4. I would expect the ordering of the words in the function names to match the existing functions, i.e. either ptr between slice and mut (my preference: cast_ptr_mut/cast_slice_ptr_mut) or ptr after mut (cast_mut_ptr/cast_slice_mut_ptr). For the second case, IMO it would read better if cast_ptr/cast_slice_ptr had const where the *mut versions have mut.
  5. cast_slice_ptr and cast_slice_ptr_mut could not (currently) be (fully) implemented on stable AFAIK, since there is no (fully "blessed") way to get the length of a general slice pointer on stable (<*const [T]>::len is unstable. There are some partial workarounds/polyfills listed on the tracking issue but none are completely general and safe.).
    • Also, making a raw slice pointer with std::ptr::slice_from_raw_parts was only stabilized in Rust 1.42.0 which is above bytemuck's MSRV of 1.34.0, so the slice pointer functions would probably have to be behind a feature flag anyway.
  6. Raw slice pointers are allowed to have element-lengths that would give them a byte-length longer than usize::MAX bytes, which would introduce an additional failure case to cast_slice_ptr(_mut) that doesn't exist for other casts: "Casting this slice pointer from the source to destination type would overflow the element-length of the slice", though this could reasonably be folded into PodCastError::SizeMismatch I suppose (PodCastError is not #[non_exhaustive], so a new variant could not be added semver-compatibly), or a new error type could be added similar to CheckedCastError. Example:
let too_long: *const [u32] = std::ptr::slice_from_raw_parts(std::ptr::null(), usize::MAX);
let what_error_should_this_return: Result<*const [u8], PodCastError> = try_cast_slice_ptr(too_long);
Lokathor commented 1 year ago

Yeah, there's a lot more small design work than it might seem at first.

Personally, for volatile access i have the voladdress crate, which has always served my needs well enough to not bother with raw pointers in bytemuck

simonask commented 8 months ago

If I may, I would suggest to leave slice pointer casts out for now, but add support for NonNull pointers.

I'm doing this in some of my projects, and I'm basically copy-pasting cast_ref and friends. These would be trivial additions.

zachs18 commented 8 months ago

there is no (fully "blessed") way to get the length of a general slice pointer on stable.

Since Rust 1.75.0, with the stabilization of wrapping_byte_add it is possible to write a correct polyfill of <*mut [T]>::len on stable.

https://github.com/rust-lang/rust/issues/71146#issuecomment-1871542032