From 24ce341185d929e0c3f14cce73ce7aac9ede0bf8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 2 Jun 2024 16:43:29 +0200 Subject: [PATCH 1/3] from_ref, from_mut: clarify domain of quantification --- library/core/src/ptr/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 8e3447d0b1b24..f1a1b1a414935 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -777,8 +777,8 @@ where /// Convert a reference to a raw pointer. /// -/// This is equivalent to `r as *const T`, but is a bit safer since it will never silently change -/// type or mutability, in particular if the code is refactored. +/// For `r: &T`, `from_ref(r)` is equivalent to `r as *const T`, but is a bit safer since it will +/// never silently change type or mutability, in particular if the code is refactored. #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] @@ -791,8 +791,8 @@ pub const fn from_ref(r: &T) -> *const T { /// Convert a mutable reference to a raw pointer. /// -/// This is equivalent to `r as *mut T`, but is a bit safer since it will never silently change -/// type or mutability, in particular if the code is refactored. +/// For `r: &mut T`, `from_mut(r)` is equivalent to `r as *mut T`, but is a bit safer since it will +/// never silently change type or mutability, in particular if the code is refactored. #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] From 64e7337be596cb12aa8c7f593a1fe5d17ab089ca Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 4 Jun 2024 07:37:24 +0200 Subject: [PATCH 2/3] more explicitly state the basic rules of working with the obtained raw pointers --- library/core/src/ptr/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index f1a1b1a414935..c6af6617bab79 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -779,6 +779,15 @@ where /// /// For `r: &T`, `from_ref(r)` is equivalent to `r as *const T`, but is a bit safer since it will /// never silently change type or mutability, in particular if the code is refactored. +/// +/// The caller must ensure that the pointee outlives the pointer this function returns, or else it +/// will end up pointing to garbage. +/// +/// The caller must also ensure that the memory the pointer (non-transitively) points to is never +/// written to (except inside an `UnsafeCell`) using this pointer or any pointer derived from it. If +/// you need to mutate the pointee, use [`from_mut`]`. Specifically, to turn a mutable reference `m: +/// &mut T` into `*const T`, prefer `from_mut(m).cast_const()` to obtain a pointer that can later be +/// used for mutation. #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] @@ -791,6 +800,9 @@ pub const fn from_ref(r: &T) -> *const T { /// Convert a mutable reference to a raw pointer. /// +/// The caller must ensure that the pointee outlives the pointer this function returns, or else it +/// will end up pointing to garbage. +/// /// For `r: &mut T`, `from_mut(r)` is equivalent to `r as *mut T`, but is a bit safer since it will /// never silently change type or mutability, in particular if the code is refactored. #[inline(always)] From 05b7f282e8a883005237833f3bbc972072111e74 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 6 Jun 2024 08:04:34 +0200 Subject: [PATCH 3/3] less garbage, more examples --- library/alloc/src/vec/mod.rs | 4 +- library/core/src/ptr/mod.rs | 80 ++++++++++++++++++++++++++++++++--- library/core/src/slice/mod.rs | 4 +- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index aa9b632cbed9e..7ecc8855dbc68 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1276,7 +1276,7 @@ impl Vec { /// valid for zero sized reads if the vector didn't allocate. /// /// The caller must ensure that the vector outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// Modifying the vector may cause its buffer to be reallocated, /// which would also make any pointers to it invalid. /// @@ -1336,7 +1336,7 @@ impl Vec { /// raw pointer valid for zero sized reads if the vector didn't allocate. /// /// The caller must ensure that the vector outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// Modifying the vector may cause its buffer to be reallocated, /// which would also make any pointers to it invalid. /// diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index c6af6617bab79..0a20b3c04c8ea 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -777,17 +777,51 @@ where /// Convert a reference to a raw pointer. /// -/// For `r: &T`, `from_ref(r)` is equivalent to `r as *const T`, but is a bit safer since it will -/// never silently change type or mutability, in particular if the code is refactored. +/// For `r: &T`, `from_ref(r)` is equivalent to `r as *const T` (except for the caveat noted below), +/// but is a bit safer since it will never silently change type or mutability, in particular if the +/// code is refactored. /// /// The caller must ensure that the pointee outlives the pointer this function returns, or else it -/// will end up pointing to garbage. +/// will end up dangling. /// /// The caller must also ensure that the memory the pointer (non-transitively) points to is never /// written to (except inside an `UnsafeCell`) using this pointer or any pointer derived from it. If /// you need to mutate the pointee, use [`from_mut`]`. Specifically, to turn a mutable reference `m: /// &mut T` into `*const T`, prefer `from_mut(m).cast_const()` to obtain a pointer that can later be /// used for mutation. +/// +/// ## Interaction with lifetime extension +/// +/// Note that this has subtle interactions with the rules for lifetime extension of temporaries in +/// tail expressions. This code is valid, albeit in a non-obvious way: +/// ```rust +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` has its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = &foo() as *const T; +/// unsafe { p.read() }; +/// ``` +/// Naively replacing the cast with `from_ref` is not valid: +/// ```rust,no_run +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` does *not* have its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = ptr::from_ref(&foo()); +/// unsafe { p.read() }; // UB! Reading from a dangling pointer ⚠️ +/// ``` +/// The recommended way to write this code is to avoid relying on lifetime extension +/// when raw pointers are involved: +/// ```rust +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// let x = foo(); +/// let p = ptr::from_ref(&x); +/// unsafe { p.read() }; +/// ``` #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] @@ -800,11 +834,45 @@ pub const fn from_ref(r: &T) -> *const T { /// Convert a mutable reference to a raw pointer. /// +/// For `r: &mut T`, `from_mut(r)` is equivalent to `r as *mut T` (except for the caveat noted +/// below), but is a bit safer since it will never silently change type or mutability, in particular +/// if the code is refactored. +/// /// The caller must ensure that the pointee outlives the pointer this function returns, or else it -/// will end up pointing to garbage. +/// will end up dangling. +/// +/// ## Interaction with lifetime extension /// -/// For `r: &mut T`, `from_mut(r)` is equivalent to `r as *mut T`, but is a bit safer since it will -/// never silently change type or mutability, in particular if the code is refactored. +/// Note that this has subtle interactions with the rules for lifetime extension of temporaries in +/// tail expressions. This code is valid, albeit in a non-obvious way: +/// ```rust +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` has its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = &mut foo() as *mut T; +/// unsafe { p.write(T::default()) }; +/// ``` +/// Naively replacing the cast with `from_mut` is not valid: +/// ```rust,no_run +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` does *not* have its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = ptr::from_mut(&mut foo()); +/// unsafe { p.write(T::default()) }; // UB! Writing to a dangling pointer ⚠️ +/// ``` +/// The recommended way to write this code is to avoid relying on lifetime extension +/// when raw pointers are involved: +/// ```rust +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// let mut x = foo(); +/// let p = ptr::from_mut(&mut x); +/// unsafe { p.write(T::default()) }; +/// ``` #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 503107c74803b..8ada77c34b353 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -731,7 +731,7 @@ impl [T] { /// Returns a raw pointer to the slice's buffer. /// /// The caller must ensure that the slice outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// /// The caller must also ensure that the memory the pointer (non-transitively) points to /// is never written to (except inside an `UnsafeCell`) using this pointer or any pointer @@ -766,7 +766,7 @@ impl [T] { /// Returns an unsafe mutable pointer to the slice's buffer. /// /// The caller must ensure that the slice outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// /// Modifying the container referenced by this slice may cause its buffer /// to be reallocated, which would also make any pointers to it invalid.