- Feature Name:
unsafe_fields
- Start Date: 2023-07-13
- RFC PR: rust-lang/rfcs#0000
- Rust Issue: rust-lang/rust#0000
Fields may be declared unsafe
. Unsafe fields may only be mutated (excluding interior mutability)
or initialized in an unsafe context. Reading the value of an unsafe field may occur in either safe
or unsafe contexts. An unsafe field may be relied upon as a safety invariant in other unsafe code.
Emphasis on safety is a key strength of Rust. A major point here is that any code path that can
result in undefined behavior must be explicitly marked with the unsafe
keyword. However, the
current system is insufficient. While Rust provides the unsafe
keyword at the function level,
there is currently no mechanism to mark fields as unsafe
.
For a real-world example, consider the Vec
type in the standard library. It has a len
field that
is used to store the number of elements present. Setting this field is exposed publicly in the
Vec::set_len
method, which has safety requirements:
new_len
must be less than or equal tocapacity()
.- The elements at
old_len..new_len
must be initialized.
This field is safe to read, but unsafe to mutate or initialize due to the invariants. These
invariants cannot be expressed in the type system, so they must be enforced manually. Failure to do
so may result in undefined behavior elsewhere in Vec
.
By introducing unsafe fields, Rust can improve the situation where a field that is otherwise safe is used as a safety invariant.
Fields may be declared unsafe
. Unsafe fields may only be initialized or accessed mutably in an
unsafe context. Reading the value of an unsafe field may occur in either safe or unsafe contexts. An
unsafe field may be relied upon as a safety invariant in other unsafe code.
Here is an example to illustrate usage:
struct Foo {
safe_field: u32,
/// Safety: Value must be an odd number.
unsafe unsafe_field: u32,
}
// Unsafe field initialization requires an `unsafe` block.
// Safety: `unsafe_field` is odd.
let mut foo = unsafe {
Foo {
safe_field: 0,
unsafe_field: 1,
}
};
// Safe field: no unsafe block.
foo.safe_field = 1;
// Unsafe field with mutation: unsafe block is required.
// Safety: The value is odd.
unsafe { foo.unsafe_field = 3; }
// Unsafe field without mutation: no unsafe block.
println!("{}", foo.unsafe_field);
For a full description of where a mutable access is considered to have occurred (and why), see RFC 3323. Keep in mind that due to reborrowing, a mutable access of an unsafe field is not necessarily explicit.
fn change_unsafe_field(foo: &mut Foo) {
// Safety: An odd number plus two remains an odd number.
unsafe { foo.unsafe_field += 2; }
}
Using the syntax from the reference for structs, the change needed to support unsafe fields is minimal.
StructField :
OuterAttribute*
Visibility?
+ unsafe?
IDENTIFIER : Type
TupleField :
OuterAttribute*
Visibility?
+ unsafe?
Type
An unsafe field may only be mutated or initialized in an unsafe context. Failure to do so is a compile error.
The concept of a "mutable use" already exists within the compiler. This catches all
situations that are relevant here, including ptr::addr_of_mut!
, &mut
, and direct assignment to a
field, while excluding interior mutability. As such, formal semantics of what constitutes a "mutable
use" are not stated here.
- Additional syntax for macros to handle
- More syntax to learn
Some items in the Rust standard library have #[rustc_layout_scalar_valid_range_start]
,
#[rustc_layout_scalar_valid_range_end]
, or both. These items have identical behavior to that of
unsafe fields described here. It is likely (though not required by this RFC) that these items will
be required to use unsafe fields, which would reduce special-casing of the standard library.
- If the syntax for restrictions does not change, what is the ordering of keywords on a field that is both unsafe and mut-restricted?
- Are there any interactions or edge cases with other language features that need to be considered?
??