[PATCH v2 1/4] rust: aref: document safety of ARef doctest example
From: Cian McGuire
Date: Fri Jul 24 2026 - 19:38:53 EST
The `into_raw` doctest contains two `unsafe` items whose SAFETY comments
were left as "TODO.": the `AlwaysRefCounted` impl for the example's
`Empty` type, and the subsequent `ARef::from_raw` call.
Both are sound for the same reason: `Empty`'s `dec_ref` is a no-op and
never deallocates anything. `AlwaysRefCounted`'s "keep alive until
matching decrement" guarantee therefore holds vacuously here -- no
decrement in this impl ever frees the object, so it cannot be freed
while a live increment remains. The object's actual lifetime is governed
entirely by ordinary Rust scoping instead: `data` is a stack value that
outlives every use of `ptr`/`data_ref`/`raw_ptr`, none of which is ever
dereferenced in the example.
The same fact justifies the `from_raw` call. Elsewhere in this file,
`Clone` and `From<&T>` justify their own calls to `from_raw` by pointing
at a preceding `inc_ref()` call, but no such call happens in this
example. That is fine here specifically because `Empty` does not track a
real reference count at all, so there is no accounting for `from_raw` to
violate, regardless of whether an increment "really" occurred.
Suggested-by: Miguel Ojeda <ojeda@xxxxxxxxxx>
Link: https://github.com/Rust-for-Linux/linux/issues/351
Signed-off-by: Cian McGuire <cianmcguire167@xxxxxxxxx>
---
v2:
- Reworked the `AlwaysRefCounted for Empty` comment: v1 only argued the
`dec_ref` half of the trait's contract; now it also explains why the
no-op `dec_ref` means nothing is ever freed through this impl, so the
"keep alive until matching decrement" guarantee cannot be violated,
and notes neither pointer is dereferenced in the example.
- Reworded the `from_raw` comment to state concretely which invariant is
vacuous, instead of just asserting "vacuously satisfied".
rust/kernel/sync/aref.rs | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs
index b721b2e00b98..37dcd0a0f406 100644
--- a/rust/kernel/sync/aref.rs
+++ b/rust/kernel/sync/aref.rs
@@ -124,7 +124,11 @@ pub unsafe fn from_raw(ptr: NonNull<T>) -> Self {
///
/// struct Empty {}
///
- /// # // SAFETY: TODO.
+ /// # // SAFETY: `dec_ref` is a no-op and never deallocates `data`, so the object's
+ /// # // lifetime is governed entirely by ordinary Rust scoping, not by this impl; the
+ /// # // trait's "keep alive until matching decrement" guarantee holds vacuously because
+ /// # // no decrement here ever frees anything, and neither `ptr` nor `raw_ptr` is ever
+ /// # // dereferenced in this example.
/// unsafe impl AlwaysRefCounted for Empty {
/// fn inc_ref(&self) {}
/// unsafe fn dec_ref(_obj: NonNull<Self>) {}
@@ -132,7 +136,9 @@ pub unsafe fn from_raw(ptr: NonNull<T>) -> Self {
///
/// let mut data = Empty {};
/// let ptr = NonNull::<Empty>::new(&mut data).unwrap();
- /// # // SAFETY: TODO.
+ /// # // SAFETY: `from_raw`'s contract concerns a reference count that `Empty` doesn't
+ /// # // actually have; since `inc_ref`/`dec_ref` never touch any state, there is no
+ /// # // invariant here for `from_raw` to violate.
/// let data_ref: ARef<Empty> = unsafe { ARef::from_raw(ptr) };
/// let raw_ptr: NonNull<Empty> = ARef::into_raw(data_ref);
///
--
2.55.0