Skip to content

Commit bee964c

Browse files
committed
Clarify unaligned fields in ptr::read_unaligned.
1 parent 848e0a2 commit bee964c

File tree

1 file changed

+53
-35
lines changed

1 file changed

+53
-35
lines changed

src/libcore/ptr/mod.rs

+53-35
Original file line numberDiff line numberDiff line change
@@ -647,42 +647,50 @@ pub unsafe fn read<T>(src: *const T) -> T {
647647
/// [read-ownership]: ./fn.read.html#ownership-of-the-returned-value
648648
/// [valid]: ../ptr/index.html#safety
649649
///
650-
/// # Examples
650+
/// ## On `packed` structs
651651
///
652-
/// Access members of a packed struct by reference:
652+
/// It is currently impossible to create raw pointers to unaligned fields
653+
/// of a packed struct.
653654
///
654-
/// ```
655-
/// use std::ptr;
655+
/// Attempting to create a raw pointer to an `unaligned` struct field with
656+
/// an expression such as `&packed.unaligned as *const FieldType` creates an
657+
/// intermediate unaligned reference before converting that to a raw pointer.
658+
/// That this reference is temporary and immediately cast is inconsequential
659+
/// as the compiler always expects references to be properly aligned.
660+
/// As a result, using `&packed.unaligned as *const FieldType` causes immediate
661+
/// *undefined behavior* in your program.
656662
///
663+
/// An example of what not to do and how this relates to `read_unaligned` is:
664+
///
665+
/// ```
657666
/// #[repr(packed, C)]
658667
/// struct Packed {
659668
/// _padding: u8,
660669
/// unaligned: u32,
661670
/// }
662671
///
663-
/// let x = Packed {
672+
/// let packed = Packed {
664673
/// _padding: 0x00,
665674
/// unaligned: 0x01020304,
666675
/// };
667676
///
668677
/// let v = unsafe {
669-
/// // Take the address of a 32-bit integer which is not aligned.
670-
/// // This must be done as a raw pointer; unaligned references are invalid.
671-
/// let unaligned = &x.unaligned as *const u32;
678+
/// // Here we attempt to take the address of a 32-bit integer which is not aligned.
679+
/// let unaligned =
680+
/// // A temporary unaligned reference is created here which results in
681+
/// // undefined behavior regardless of whether the reference is used or not.
682+
/// &packed.unaligned
683+
/// // Casting to a raw pointer doesn't help; the mistake already happened.
684+
/// as *const u32;
672685
///
673-
/// // Dereferencing normally will emit an aligned load instruction,
674-
/// // causing undefined behavior.
675-
/// // let v = *unaligned; // ERROR
676-
///
677-
/// // Instead, use `read_unaligned` to read improperly aligned values.
678-
/// let v = ptr::read_unaligned(unaligned);
686+
/// let v = std::ptr::read_unaligned(unaligned);
679687
///
680688
/// v
681689
/// };
682-
///
683-
/// // Accessing unaligned values directly is safe.
684-
/// assert!(x.unaligned == v);
685690
/// ```
691+
///
692+
/// Accessing unaligned values directly with e.g. `packed.unaligned` is safe however.
693+
// FIXME: Update docs based on outcome of RFC #2582 and friends.
686694
#[inline]
687695
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
688696
pub unsafe fn read_unaligned<T>(src: *const T) -> T {
@@ -811,38 +819,48 @@ pub unsafe fn write<T>(dst: *mut T, src: T) {
811819
///
812820
/// [valid]: ../ptr/index.html#safety
813821
///
814-
/// # Examples
822+
/// ## On `packed` structs
815823
///
816-
/// Access fields in a packed struct:
824+
/// It is currently impossible to create raw pointers to unaligned fields
825+
/// of a packed struct.
817826
///
818-
/// ```
819-
/// use std::{mem, ptr};
827+
/// Attempting to create a raw pointer to an `unaligned` struct field with
828+
/// an expression such as `&packed.unaligned as *const FieldType` creates an
829+
/// intermediate unaligned reference before converting that to a raw pointer.
830+
/// That this reference is temporary and immediately cast is inconsequential
831+
/// as the compiler always expects references to be properly aligned.
832+
/// As a result, using `&packed.unaligned as *const FieldType` causes immediate
833+
/// *undefined behavior* in your program.
820834
///
835+
/// An example of what not to do and how this relates to `write_unaligned` is:
836+
///
837+
/// ```
821838
/// #[repr(packed, C)]
822-
/// #[derive(Default)]
823839
/// struct Packed {
824840
/// _padding: u8,
825841
/// unaligned: u32,
826842
/// }
827843
///
828844
/// let v = 0x01020304;
829-
/// let mut x: Packed = unsafe { mem::zeroed() };
845+
/// let mut packed: Packed = unsafe { std::mem::zeroed() };
830846
///
831-
/// unsafe {
832-
/// // Take a reference to a 32-bit integer which is not aligned.
833-
/// let unaligned = &mut x.unaligned as *mut u32;
847+
/// let v = unsafe {
848+
/// // Here we attempt to take the address of a 32-bit integer which is not aligned.
849+
/// let unaligned =
850+
/// // A temporary unaligned reference is created here which results in
851+
/// // undefined behavior regardless of whether the reference is used or not.
852+
/// &mut packed.unaligned
853+
/// // Casting to a raw pointer doesn't help; the mistake already happened.
854+
/// as *mut u32;
834855
///
835-
/// // Dereferencing normally will emit an aligned store instruction,
836-
/// // causing undefined behavior because the pointer is not aligned.
837-
/// // *unaligned = v; // ERROR
856+
/// std::ptr::write_unaligned(unaligned, v);
838857
///
839-
/// // Instead, use `write_unaligned` to write improperly aligned values.
840-
/// ptr::write_unaligned(unaligned, v);
841-
/// }
842-
///
843-
/// // Accessing unaligned values directly is safe.
844-
/// assert!(x.unaligned == v);
858+
/// v
859+
/// };
845860
/// ```
861+
///
862+
/// Accessing unaligned values directly with e.g. `packed.unaligned` is safe however.
863+
// FIXME: Update docs based on outcome of RFC #2582 and friends.
846864
#[inline]
847865
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
848866
pub unsafe fn write_unaligned<T>(dst: *mut T, src: T) {

0 commit comments

Comments
 (0)