diff --git a/ci/check_readme.sh b/ci/check_readme.sh index 241128d0d0..413f5a866d 100755 --- a/ci/check_readme.sh +++ b/ci/check_readme.sh @@ -16,4 +16,6 @@ set -eo pipefail cargo install -q cargo-readme --version 3.2.0 diff <(cargo -q run --manifest-path tools/Cargo.toml -p generate-readme) README.md >&2 -exit $? + +cd unsafe-fields +diff <(cargo -q run --manifest-path ../tools/Cargo.toml -p generate-readme) README.md >&2 diff --git a/unsafe-fields/Cargo.toml b/unsafe-fields/Cargo.toml index adc98bbff5..43ab479958 100644 --- a/unsafe-fields/Cargo.toml +++ b/unsafe-fields/Cargo.toml @@ -8,7 +8,7 @@ [package] name = "unsafe-fields" -version = "0.2.0" +version = "0.2.1" edition = "2021" description = "Make it unsafe to access or modify fields with safety invariants" license = "BSD-2-Clause OR Apache-2.0 OR MIT" diff --git a/unsafe-fields/README.md b/unsafe-fields/README.md new file mode 100644 index 0000000000..ce3b2ae451 --- /dev/null +++ b/unsafe-fields/README.md @@ -0,0 +1,112 @@ + + +# unsafe-fields + +Support for unsafe fields. + +This crate provides the `unsafe_fields!` macro, which can be used to mark +fields as unsafe. Unsafe fields automatically have their types wrapped using +the `Unsafe` wrapper type. An `Unsafe` is intended to be used to for +struct, enum, or union fields which carry safety invariants. All accessors +are `unsafe`, which requires any use of an `Unsafe` field to be inside an +`unsafe` block. One exception is `Unsafe::as_ref`, which is available when +the `zerocopy_0_8` feature is enabled. See its docs for more information. + +An unsafe field has the type `Unsafe`. `O` is +the enclosing type (struct, enum, or union), `F` is the type of the field, +and `NAME_HASH` is the hash of the field's name. `O` prevents swapping +unsafe fields of the same `F` type between different enclosing types, and +`NAME_HASH` prevents swapping different fields of the same `F` type within +the same enclosing type. Note that swapping the same field between instances +of the same type [cannot be prevented](crate#limitations). + +[immutable]: zerocopy_0_8::Immutable + +## Examples + +```rust +use unsafe_fields::{unsafe_fields, Unsafe}; + +unsafe_fields! { + /// A `usize` which is guaranteed to be even. + pub struct EvenUsize { + // INVARIANT: `n` is even. + #[unsafe] + n: usize, + } +} + +impl EvenUsize { + /// Constructs a new `EvenUsize`. + /// + /// Returns `None` if `n` is odd. + pub fn new(n: usize) -> Option { + if n % 2 != 0 { + return None; + } + // SAFETY: We just confirmed that `n` is even. + let n = unsafe { Unsafe::new(n) }; + Some(EvenUsize { n }) + } +} +``` + +Attempting to swap unsafe fields of the same type is prevented: + +```compile_fail,E0308 +use unsafe_fields::{unsafe_fields, Unsafe}; + +unsafe_fields! { + /// A range. + pub struct Range { + // INVARIANT: `lo <= hi`. + #[unsafe] + lo: usize, + #[unsafe] + hi: usize, + } +} + +impl Range { + pub fn swap(&mut self) { + // ERROR: Mismatched types + core::mem::swap(&mut self.lo, &mut self.hi); + } +} +``` + +## Limitations + +Note that we cannot prevent `Unsafe`s from being swapped between the same +field in instances of the same type: + +```rust +use unsafe_fields::{unsafe_fields, Unsafe}; + +unsafe_fields! { + /// A `usize` which is guaranteed to be even. + pub struct EvenUsize { + // INVARIANT: `n` is even. + #[unsafe] + n: usize, + } +} + +pub fn swap(a: &mut EvenUsize, b: &mut EvenUsize) { + core::mem::swap(&mut a.n, &mut b.n); +} +``` + +## Disclaimer + +Disclaimer: Zerocopy is not an officially supported Google product. diff --git a/unsafe-fields/src/lib.rs b/unsafe-fields/src/lib.rs index 65bdb76b9f..e8adb1bcc3 100644 --- a/unsafe-fields/src/lib.rs +++ b/unsafe-fields/src/lib.rs @@ -6,6 +6,11 @@ // This file may not be copied, modified, or distributed except according to // those terms. +// After updating the following doc comment, make sure to run the following +// command to update `README.md` based on its contents: +// +// cargo -q run --manifest-path ../tools/Cargo.toml -p generate-readme > README.md + //! Support for unsafe fields. //! //! This crate provides the [`unsafe_fields!`] macro, which can be used to mark