Skip to content

Commit ed920e9

Browse files
committed
RFC3458: Add complete, realistic example
1 parent f04701b commit ed920e9

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

text/0000-unsafe-fields.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,130 @@ We might also imagine a variant of the above example where `alignment_pow`, like
399399
carry a safety invariant. Ultimately, whether or not it makes sense for a field to be `unsafe` is a
400400
function of programmer preference and API requirements.
401401

402+
## Complete Example
403+
404+
The below example demonstrates how field safety support can be applied to build a practical
405+
abstraction with small safety boundaries
406+
([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=e8aa2af933f5bf4892d1be951062538d)):
407+
408+
```rust
409+
#![deny(
410+
unfulfilled_lint_expectations,
411+
clippy::missing_safety_doc,
412+
clippy::undocumented_unsafe_blocks,
413+
)]
414+
415+
use std::{
416+
cell::UnsafeCell,
417+
ops::{Deref, DerefMut},
418+
sync::Arc,
419+
};
420+
421+
/// An `Arc` that provides exclusive access to its referent.
422+
///
423+
/// A `UniqueArc` may have any number of `KeepAlive` handles which ensure that
424+
/// the inner value is not dropped. These handles only control dropping, and do
425+
/// not provide read or write access to the value.
426+
pub struct UniqueArc<T: 'static> {
427+
/// # Safety
428+
///
429+
/// So long as `T` is owned by `UniqueArc`, `T` may not be accessed (read or
430+
/// written) other than via this `UniqueArc`.
431+
unsafe arc: Arc<UnsafeCell<T>>,
432+
}
433+
434+
/// Keeps the parent [`UniqueArc`] alive without providing read or write access
435+
/// to its value.
436+
pub struct KeepAlive<T> {
437+
/// # Safety
438+
///
439+
/// `T` may not be accessed (read or written) via this `Arc`.
440+
#[expect(unused)]
441+
unsafe arc: Arc<UnsafeCell<T>>,
442+
}
443+
444+
impl<T> UniqueArc<T> {
445+
/// Constructs a new `UniqueArc` from a value.
446+
pub fn new(val: T) -> Self {
447+
let arc = Arc::new(UnsafeCell::new(val));
448+
// SAFETY: Since we have just created `arc` and have neither cloned it
449+
// nor leaked a reference to it, we can be sure `T` cannot be read or
450+
// accessed other than via this particular `arc`.
451+
unsafe { Self { arc } }
452+
}
453+
454+
/// Releases ownership of the enclosed value.
455+
///
456+
/// Returns `None` if any `KeepAlive`s were created but not destroyed.
457+
pub fn into_inner(self) -> Option<T> {
458+
// SAFETY: Moving `arc` out of `Self` releases it from its safety
459+
// invariant.
460+
let arc = unsafe { self.arc };
461+
Arc::into_inner(arc).map(UnsafeCell::into_inner)
462+
}
463+
464+
/// Produces a `KeepAlive` handle, which defers the destruction
465+
/// of the enclosed value.
466+
pub fn keep_alive(&self) -> KeepAlive<T> {
467+
// SAFETY: By invariant on `KeepAlive::arc`, this clone will never be
468+
// used for accessing `T`, as required by `UniqueArc::arc`. The one
469+
// exception is that, if a `KeepAlive` is the last reference to be
470+
// dropped, then it will drop the inner `T`. However, if this happens,
471+
// it means that the `UniqueArc` has already been dropped, and so its
472+
// invariant will not be violated.
473+
unsafe {
474+
KeepAlive {
475+
arc: self.arc.clone(),
476+
}
477+
}
478+
}
479+
}
480+
481+
impl<T> Deref for UniqueArc<T> {
482+
type Target = T;
483+
484+
fn deref(&self) -> &T {
485+
// SAFETY: We do not create any other owning references to `arc` - we
486+
// only dereference it below, but do not clone it.
487+
let arc = unsafe { &self.arc };
488+
let ptr = UnsafeCell::get(arc);
489+
// SAFETY: We satisfy all requirements for pointer-to-reference
490+
// conversions [1]:
491+
// - By invariant on `&UnsafeCell<T>`, `ptr` is well-aligned, non-null,
492+
// dereferenceable, and points to a valid `T`.
493+
// - By invariant on `Self::arc`, no other `Arc` references exist to
494+
// this value which will be used for reading or writing. Thus, we
495+
// satisfy the aliasing invariant of `&` references.
496+
//
497+
// [1] https://doc.rust-lang.org/1.85.0/std/ptr/index.html#pointer-to-reference-conversion
498+
unsafe { &*ptr }
499+
}
500+
}
501+
502+
impl<T> DerefMut for UniqueArc<T> {
503+
fn deref_mut(&mut self) -> &mut T {
504+
// SAFETY: We do not create any other owning references to `arc` - we
505+
// only dereference it below, but do not clone it.
506+
let arc = unsafe { &mut self.arc };
507+
let val = UnsafeCell::get(arc);
508+
// SAFETY: We satisfy all requirements for pointer-to-reference
509+
// conversions [1]:
510+
// - By invariant on `&mut UnsafeCell<T>`, `ptr` is well-aligned,
511+
// non-null, dereferenceable, and points to a valid `T`.
512+
// - By invariant on `Self::arc`, no other `Arc` references exist to
513+
// this value which will be used for reading or writing. Thus, we
514+
// satisfy the aliasing invariant of `&mut` references.
515+
//
516+
// [1] https://doc.rust-lang.org/1.85.0/std/ptr/index.html#pointer-to-reference-conversion
517+
unsafe { &mut *val }
518+
}
519+
}
520+
521+
// SAFETY: `UniqueArc<T>` has the same aliasing and synchronization properties
522+
// as `&mut T`, and so it is `Sync` if `&mut T` is `Sync`.
523+
unsafe impl<T> Sync for UniqueArc<T> where &'static mut T: Sync {}
524+
```
525+
402526
# Reference-level explanation
403527

404528
## Syntax

0 commit comments

Comments
 (0)