From 36d7f9330f60298b075579da738ea03f4bbc8981 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 3 Apr 2021 19:12:08 +0200 Subject: [PATCH 1/5] document raw-addr-of operators --- src/expressions/operator-expr.md | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 94bd0ca23..8ca68513f 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -71,6 +71,50 @@ let a = && && mut 10; let a = & & & & mut 10; ``` +### Raw address-of operators + +Related to the borrow operators are the raw address-of operators, which do not have first-class syntax, but are exposed via the macros `ptr::addr_of!(expr)` and `ptr::addr_of_mut!(expr)`. +Like with `&`/`&mut`, the expression is evaluated in place expression context. +The difference is that `&`/`&mut` create *references* of type `&T`/`&mut T`, while `ptr::addr_of!(expr)` creates a (const) raw pointer of type `*const T` and `ptr::addr_of_mut!(expr)` creates a mutable raw pointer of type `*mut T`. + +The raw address-of operators must be used whenever the place expression denotes a place that is not properly aligned or does not store a valid value as determined by its type. +In those situations, using a borrow operator would cause [undefined behavior] by creating an invalid reference, but a raw pointer may still be constructed using an address-of operator. + +**Example of creating a raw pointer to an unaligned place (through a `packed` struct):** + +```rust +use std::ptr; + +#[repr(packed)] +struct Packed { + f1: u8, + f2: u16, +} + +let packed = Packed { f1: 1, f2: 2 }; +// `&packed.f2` would create an unaligned reference, and thus be Undefined Behavior! +let raw_f2 = ptr::addr_of!(packed.f2); +assert_eq!(unsafe { raw_f2.read_unaligned() }, 2); +``` + +**Example of creating a raw pointer to a place that does not contain a valid value:** + +```rust +use std::{ptr, mem::MaybeUninit}; + +struct Demo { + field: bool, +} + +let mut uninit = MaybeUninit::::uninit(); +// `&uninit.as_mut().field` would create a reference to an uninitialized `bool`, +// and thus be Undefined Behavior! +let f1_ptr = unsafe { ptr::addr_of_mut!((*uninit.as_mut_ptr()).field) }; +unsafe { f1_ptr.write(true); } +let init = unsafe { uninit.assume_init() }; +``` + + ## The dereference operator > **Syntax**\ @@ -595,6 +639,7 @@ See [this test] for an example of using this dependency. [float-float]: https://github.com/rust-lang/rust/issues/15536 [Function pointer]: ../types/function-pointer.md [Function item]: ../types/function-item.md +[undefined behavior]: ../behavior-considered-undefined.md [_BorrowExpression_]: #borrow-operators [_DereferenceExpression_]: #the-dereference-operator From 6e26a8c7ffdf004eff90aab432938768be6ba54a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 6 Apr 2021 14:22:25 +0200 Subject: [PATCH 2/5] Apply suggestions from code review Co-authored-by: Ryan Scheel --- src/expressions/operator-expr.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 8ca68513f..46b036d39 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -73,14 +73,14 @@ let a = & & & & mut 10; ### Raw address-of operators -Related to the borrow operators are the raw address-of operators, which do not have first-class syntax, but are exposed via the macros `ptr::addr_of!(expr)` and `ptr::addr_of_mut!(expr)`. +Related to the borrow operators are the *raw address-of operators*, which do not have first-class syntax, but are exposed via the macros `ptr::addr_of!(expr)` and `ptr::addr_of_mut!(expr)`. Like with `&`/`&mut`, the expression is evaluated in place expression context. The difference is that `&`/`&mut` create *references* of type `&T`/`&mut T`, while `ptr::addr_of!(expr)` creates a (const) raw pointer of type `*const T` and `ptr::addr_of_mut!(expr)` creates a mutable raw pointer of type `*mut T`. -The raw address-of operators must be used whenever the place expression denotes a place that is not properly aligned or does not store a valid value as determined by its type. +The raw address-of operators must be used whenever the place expression could evaluate to a place that is not properly aligned or does not store a valid value as determined by its type. In those situations, using a borrow operator would cause [undefined behavior] by creating an invalid reference, but a raw pointer may still be constructed using an address-of operator. -**Example of creating a raw pointer to an unaligned place (through a `packed` struct):** +The following is an example of creating a raw pointer to an unaligned place through a `packed` struct: ```rust use std::ptr; @@ -97,7 +97,7 @@ let raw_f2 = ptr::addr_of!(packed.f2); assert_eq!(unsafe { raw_f2.read_unaligned() }, 2); ``` -**Example of creating a raw pointer to a place that does not contain a valid value:** +The following is an example of creating a raw pointer to a place that does not contain a valid value: ```rust use std::{ptr, mem::MaybeUninit}; From b18545ec138c1cf848633f06f48b856a21f1a378 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 6 Apr 2021 14:26:46 +0200 Subject: [PATCH 3/5] edits --- src/expressions.md | 3 ++- src/expressions/operator-expr.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/expressions.md b/src/expressions.md index 5b4092984..32ee658ff 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -157,7 +157,7 @@ A *value expression* is an expression that represents an actual value. The following contexts are *place expression* contexts: * The left operand of a [compound assignment] expression. -* The operand of a unary [borrow] or [dereference][deref] operator. +* The operand of a unary [borrow], [address-of][addr-of] or [dereference][deref] operator. * The operand of a field expression. * The indexed operand of an array indexing expression. * The operand of any [implicit borrow]. @@ -308,6 +308,7 @@ They are never allowed before: [assign]: expressions/operator-expr.md#assignment-expressions [borrow]: expressions/operator-expr.md#borrow-operators +[addr-of]: expressions/operator-expr.md#raw-address-of-operators [comparison]: expressions/operator-expr.md#comparison-operators [compound assignment]: expressions/operator-expr.md#compound-assignment-expressions [deref]: expressions/operator-expr.md#the-dereference-operator diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 46b036d39..7fbc8400c 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -74,7 +74,7 @@ let a = & & & & mut 10; ### Raw address-of operators Related to the borrow operators are the *raw address-of operators*, which do not have first-class syntax, but are exposed via the macros `ptr::addr_of!(expr)` and `ptr::addr_of_mut!(expr)`. -Like with `&`/`&mut`, the expression is evaluated in place expression context. +Like with `&`/`&mut`, the expression `expr` is evaluated in place expression context. The difference is that `&`/`&mut` create *references* of type `&T`/`&mut T`, while `ptr::addr_of!(expr)` creates a (const) raw pointer of type `*const T` and `ptr::addr_of_mut!(expr)` creates a mutable raw pointer of type `*mut T`. The raw address-of operators must be used whenever the place expression could evaluate to a place that is not properly aligned or does not store a valid value as determined by its type. From 74f408238bcfff876da268bd821de111df914f57 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 7 Apr 2021 14:50:14 +0200 Subject: [PATCH 4/5] further tweak addr_of exposition --- src/expressions/operator-expr.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 7fbc8400c..05ce1dcd2 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -74,10 +74,10 @@ let a = & & & & mut 10; ### Raw address-of operators Related to the borrow operators are the *raw address-of operators*, which do not have first-class syntax, but are exposed via the macros `ptr::addr_of!(expr)` and `ptr::addr_of_mut!(expr)`. -Like with `&`/`&mut`, the expression `expr` is evaluated in place expression context. -The difference is that `&`/`&mut` create *references* of type `&T`/`&mut T`, while `ptr::addr_of!(expr)` creates a (const) raw pointer of type `*const T` and `ptr::addr_of_mut!(expr)` creates a mutable raw pointer of type `*mut T`. +The expression `expr` is evaluated in place expression context. +`ptr::addr_of!(expr)` then creates a (const) raw pointer of type `*const T` to the given place, and `ptr::addr_of_mut!(expr)` creates a mutable raw pointer of type `*mut T`. -The raw address-of operators must be used whenever the place expression could evaluate to a place that is not properly aligned or does not store a valid value as determined by its type. +The raw address-of operators must be used instead of a borrow operator whenever the place expression could evaluate to a place that is not properly aligned or does not store a valid value as determined by its type, or whenever creating a reference would introduce incorrect aliasing assumptions. In those situations, using a borrow operator would cause [undefined behavior] by creating an invalid reference, but a raw pointer may still be constructed using an address-of operator. The following is an example of creating a raw pointer to an unaligned place through a `packed` struct: From 5bf665d3fb53b8e8dde9abff6f04b5bc07bb3c7c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Feb 2022 21:52:12 -0500 Subject: [PATCH 5/5] tweaks --- src/expressions/operator-expr.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 05ce1dcd2..331f4c700 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -73,9 +73,9 @@ let a = & & & & mut 10; ### Raw address-of operators -Related to the borrow operators are the *raw address-of operators*, which do not have first-class syntax, but are exposed via the macros `ptr::addr_of!(expr)` and `ptr::addr_of_mut!(expr)`. +Related to the borrow operators are the *raw address-of operators*, which do not have first-class syntax, but are exposed via the macros [`ptr::addr_of!(expr)`][addr_of] and [`ptr::addr_of_mut!(expr)`][addr_of_mut]. The expression `expr` is evaluated in place expression context. -`ptr::addr_of!(expr)` then creates a (const) raw pointer of type `*const T` to the given place, and `ptr::addr_of_mut!(expr)` creates a mutable raw pointer of type `*mut T`. +`ptr::addr_of!(expr)` then creates a const raw pointer of type `*const T` to the given place, and `ptr::addr_of_mut!(expr)` creates a mutable raw pointer of type `*mut T`. The raw address-of operators must be used instead of a borrow operator whenever the place expression could evaluate to a place that is not properly aligned or does not store a valid value as determined by its type, or whenever creating a reference would introduce incorrect aliasing assumptions. In those situations, using a borrow operator would cause [undefined behavior] by creating an invalid reference, but a raw pointer may still be constructed using an address-of operator. @@ -640,6 +640,8 @@ See [this test] for an example of using this dependency. [Function pointer]: ../types/function-pointer.md [Function item]: ../types/function-item.md [undefined behavior]: ../behavior-considered-undefined.md +[addr_of]: ../../std/ptr/macro.addr_of.html +[addr_of_mut]: ../../std/ptr/macro.addr_of_mut.html [_BorrowExpression_]: #borrow-operators [_DereferenceExpression_]: #the-dereference-operator