Skip to content

Commit 34f4e6e

Browse files
authored
[unsafe-fields] Add as_ref_checked (#1960)
Release 0.2.0. Makes progress on #1931 gherrit-pr-id: I7ce0c981ed1f1bc1f4ff85dffef2a74114c6e76d
1 parent f51d666 commit 34f4e6e

File tree

4 files changed

+53
-20
lines changed

4 files changed

+53
-20
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,10 @@ jobs:
523523
"stable",
524524
"nightly",
525525
]
526+
features: [
527+
"",
528+
"--features zerocopy_0_8",
529+
]
526530
steps:
527531
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
528532
- name: Configure environment variables
@@ -540,19 +544,19 @@ jobs:
540544
toolchain: ${{ env.ZC_TOOLCHAIN }}
541545
components: clippy, rust-src
542546
- name: Check
543-
run: ./cargo.sh +${{ matrix.toolchain }} check --package unsafe-fields --verbose
547+
run: ./cargo.sh +${{ matrix.toolchain }} check --package unsafe-fields ${{ matrix.features }} --verbose
544548
- name: Check tests
545-
run: ./cargo.sh +${{ matrix.toolchain }} check --tests --package unsafe-fields --verbose
549+
run: ./cargo.sh +${{ matrix.toolchain }} check --tests --package unsafe-fields ${{ matrix.features }} --verbose
546550
- name: Build
547-
run: ./cargo.sh +${{ matrix.toolchain }} build --package unsafe-fields --verbose
551+
run: ./cargo.sh +${{ matrix.toolchain }} build --package unsafe-fields ${{ matrix.features }} --verbose
548552
- name: Run tests
549-
run: ./cargo.sh +${{ matrix.toolchain }} test --package unsafe-fields --verbose
553+
run: ./cargo.sh +${{ matrix.toolchain }} test --package unsafe-fields ${{ matrix.features }} --verbose
550554
- name: Clippy
551-
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package unsafe-fields --verbose
555+
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package unsafe-fields ${{ matrix.features }} --verbose
552556
# See comment in next step for why we only run on nightly.
553557
if: matrix.toolchain == 'nightly'
554558
- name: Clippy tests
555-
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package unsafe-fields --tests --verbose
559+
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package unsafe-fields --tests ${{ matrix.features }} --verbose
556560
# Clippy improves the accuracy of lints over time, and fixes bugs. Only
557561
# running Clippy on nightly allows us to avoid having to write code
558562
# which is compatible with older versions of Clippy, which sometimes
@@ -573,7 +577,7 @@ jobs:
573577
METADATA_DOCS_RS_RUSTDOC_ARGS="$(cargo metadata --format-version 1 | \
574578
jq -r ".packages[] | select(.name == \"unsafe-fields\").metadata.docs.rs.\"rustdoc-args\"[]" | tr '\n' ' ')"
575579
export RUSTDOCFLAGS="${{ matrix.toolchain == 'nightly' && '-Z unstable-options --document-hidden-items $METADATA_DOCS_RS_RUSTDOC_ARGS'|| '' }} $RUSTDOCFLAGS"
576-
./cargo.sh +${{ matrix.toolchain }} doc --document-private-items --package unsafe-fields
580+
./cargo.sh +${{ matrix.toolchain }} doc --document-private-items --package unsafe-fields --all-features
577581
578582
# NEON intrinsics are currently broken on big-endian platforms. [1] This test ensures
579583
# that we don't accidentally attempt to compile these intrinsics on such platforms. We

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# https://github.com/dtolnay/trybuild/issues/207#issuecomment-131227.594
1313
[workspace]
1414

15+
members = ["unsafe-fields", "zerocopy-derive"]
16+
1517
[workspace.package]
1618
# Inherited by zerocopy and unsafe-fields.
1719
rust-version = "1.65.0"
@@ -85,7 +87,6 @@ testutil = { path = "testutil" }
8587
# sometimes change the output format slightly, so a version mismatch can cause
8688
# CI test failures.
8789
trybuild = { version = "=1.0.90", features = ["diff"] }
88-
unsafe-fields = { path = "./unsafe-fields" }
8990
# In tests, unlike in production, zerocopy-derive is not optional
9091
zerocopy-derive = { version = "=0.9.0-alpha.0", path = "zerocopy-derive" }
9192
# TODO(#381) Remove this dependency once we have our own layout gadgets.

unsafe-fields/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
[package]
1010
name = "unsafe-fields"
11-
version = "0.1.0"
11+
version = "0.2.0"
1212
edition = "2021"
1313
description = "Make it unsafe to access or modify fields with safety invariants"
1414
license = "BSD-2-Clause OR Apache-2.0 OR MIT"
@@ -20,3 +20,9 @@ exclude = [".*"]
2020
[package.metadata.docs.rs]
2121
all-features = true
2222
rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"]
23+
24+
[lints.rust]
25+
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(doc_cfg)'] }
26+
27+
[dependencies]
28+
zerocopy_0_8 = { package = "zerocopy", path = "..", optional = true }

unsafe-fields/src/lib.rs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
//! the [`Unsafe`] wrapper type. An `Unsafe` is intended to be used to for
1414
//! struct, enum, or union fields which carry safety invariants. All accessors
1515
//! are `unsafe`, which requires any use of an `Unsafe` field to be inside an
16-
//! `unsafe` block.
16+
//! `unsafe` block. One exception is [`Unsafe::as_ref`], which is available when
17+
//! the `zerocopy_0_8` feature is enabled. See its docs for more information.
1718
//!
1819
//! An unsafe field has the type `Unsafe<O, F, const NAME_HASH: u128>`. `O` is
1920
//! the enclosing type (struct, enum, or union), `F` is the type of the field,
@@ -23,6 +24,8 @@
2324
//! the same enclosing type. Note that swapping the same field between instances
2425
//! of the same type [cannot be prevented](crate#limitations).
2526
//!
27+
//! [immutable]: zerocopy_0_8::Immutable
28+
//!
2629
//! # Examples
2730
//!
2831
//! ```
@@ -170,6 +173,7 @@
170173
rustdoc::missing_crate_level_docs,
171174
rustdoc::private_intra_doc_links
172175
)]
176+
#![cfg_attr(doc_cfg, feature(doc_cfg))]
173177

174178
use core::marker::PhantomData;
175179

@@ -210,16 +214,41 @@ impl<O: ?Sized, F: Copy, const NAME_HASH: u128> Clone for Unsafe<O, F, { NAME_HA
210214
impl<O: ?Sized, F: ?Sized, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
211215
/// Gets a reference to the inner value.
212216
///
217+
/// If [`F: Immutable`][immutable], prefer [`as_ref`], which is safe.
218+
///
219+
/// [immutable]: zerocopy_0_8::Immutable
220+
/// [`as_ref`]: Unsafe::as_ref
221+
///
213222
/// # Safety
214223
///
215224
/// The caller is responsible for upholding any safety invariants associated
216225
/// with this field.
217226
#[inline(always)]
218-
pub const unsafe fn as_ref(&self) -> &F {
227+
pub const unsafe fn as_ref_unchecked(&self) -> &F {
219228
// SAFETY: This method is unsafe to call.
220229
&self.field
221230
}
222231

232+
/// Gets a reference to the inner value safely so long as the inner value is
233+
/// immutable.
234+
///
235+
/// If [`F: Immutable`][immutable], then `F` does not permit interior
236+
/// mutation, and so it is safe to return a reference to it.
237+
///
238+
/// [immutable]: zerocopy_0_8::Immutable
239+
#[inline(always)]
240+
#[cfg(feature = "zerocopy_0_8")]
241+
#[cfg_attr(doc_cfg, doc(cfg(feature = "zerocopy_0_8")))]
242+
pub const fn as_ref(&self) -> &F
243+
where
244+
F: zerocopy_0_8::Immutable,
245+
{
246+
// SAFETY: `F: Immutable` guarantees that the returned `&F` cannot be
247+
// used to mutate `self`, and so it cannot be used to violate any
248+
// invariant.
249+
unsafe { self.as_ref_unchecked() }
250+
}
251+
223252
/// Gets a mutable reference to the inner value.
224253
///
225254
/// # Safety
@@ -234,7 +263,7 @@ impl<O: ?Sized, F: ?Sized, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
234263
}
235264

236265
impl<O: ?Sized, F, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
237-
/// Constructs a new `Unsafe<O, F, NAME_HASH>`.
266+
/// Constructs a new `Unsafe`.
238267
///
239268
/// # Safety
240269
///
@@ -247,13 +276,8 @@ impl<O: ?Sized, F, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
247276
}
248277

249278
/// Extracts the inner `F` from `self`.
250-
///
251-
/// # Safety
252-
///
253-
/// The caller is responsible for upholding any safety invariants associated
254-
/// with this field.
255279
#[inline(always)]
256-
pub const unsafe fn into(self) -> F {
280+
pub const fn into(self) -> F {
257281
use core::mem::ManuallyDrop;
258282
let slf = ManuallyDrop::new(self);
259283

@@ -280,8 +304,6 @@ impl<O: ?Sized, F, const NAME_HASH: u128> Unsafe<O, F, { NAME_HASH }> {
280304
// validity as `T`
281305
let dst = unsafe { Transmute { src: slf }.dst };
282306

283-
// SAFETY (satisfaction of `Unsafe`'s field invariant): This method is
284-
// unsafe to call.
285307
ManuallyDrop::into_inner(dst)
286308
}
287309
}

0 commit comments

Comments
 (0)