rust: pin-init: change blanket impls for [Pin]Init and add one for Result<T, E>

Remove the error from the blanket implementations `impl<T, E> Init<T, E>
for T` (and also for `PinInit`). Add implementations for `Result<T, E>`.

This allows one to easily construct (un)conditional failing
initializers. It also improves the compatibility with APIs that do not
use pin-init, because users can supply a `Result<T, E>` to a  function
taking an `impl PinInit<T, E>`.

Suggested-by: Alice Ryhl <aliceryhl@google.com>
Link: 58612514b2
[ Also fix a compile error in block. - Benno ]
Reviewed-by: Boqun Feng <boqun.feng@gmail.com>
Link: https://lore.kernel.org/all/20250529081027.297648-2-lossin@kernel.org
[ Add title prefix `rust: pin-init`. - Benno ]
Signed-off-by: Benno Lossin <lossin@kernel.org>
This commit is contained in:
Benno Lossin 2025-05-29 10:10:24 +02:00
parent 8b3d955f72
commit e832374cca
2 changed files with 33 additions and 9 deletions

View file

@ -9,7 +9,7 @@ use core::pin::Pin;
use crate::{ use crate::{
bindings, bindings,
block::mq::{operations::OperationsVTable, request::RequestDataWrapper, Operations}, block::mq::{operations::OperationsVTable, request::RequestDataWrapper, Operations},
error, error::{self, Result},
prelude::try_pin_init, prelude::try_pin_init,
types::Opaque, types::Opaque,
}; };
@ -41,7 +41,7 @@ impl<T: Operations> TagSet<T> {
// SAFETY: `blk_mq_tag_set` only contains integers and pointers, which // SAFETY: `blk_mq_tag_set` only contains integers and pointers, which
// all are allowed to be 0. // all are allowed to be 0.
let tag_set: bindings::blk_mq_tag_set = unsafe { core::mem::zeroed() }; let tag_set: bindings::blk_mq_tag_set = unsafe { core::mem::zeroed() };
let tag_set = core::mem::size_of::<RequestDataWrapper>() let tag_set: Result<_> = core::mem::size_of::<RequestDataWrapper>()
.try_into() .try_into()
.map(|cmd_size| { .map(|cmd_size| {
bindings::blk_mq_tag_set { bindings::blk_mq_tag_set {
@ -56,12 +56,14 @@ impl<T: Operations> TagSet<T> {
nr_maps: num_maps, nr_maps: num_maps,
..tag_set ..tag_set
} }
}); })
.map(Opaque::new)
.map_err(|e| e.into());
try_pin_init!(TagSet { try_pin_init!(TagSet {
inner <- PinInit::<_, error::Error>::pin_chain(Opaque::new(tag_set?), |tag_set| { inner <- tag_set.pin_chain(|tag_set| {
// SAFETY: we do not move out of `tag_set`. // SAFETY: we do not move out of `tag_set`.
let tag_set = unsafe { Pin::get_unchecked_mut(tag_set) }; let tag_set: &mut Opaque<_> = unsafe { Pin::get_unchecked_mut(tag_set) };
// SAFETY: `tag_set` is a reference to an initialized `blk_mq_tag_set`. // SAFETY: `tag_set` is a reference to an initialized `blk_mq_tag_set`.
error::to_result( unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())}) error::to_result( unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())})
}), }),

View file

@ -1391,8 +1391,8 @@ where
} }
// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`. // SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`.
unsafe impl<T, E> Init<T, E> for T { unsafe impl<T> Init<T> for T {
unsafe fn __init(self, slot: *mut T) -> Result<(), E> { unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> {
// SAFETY: `slot` is valid for writes by the safety requirements of this function. // SAFETY: `slot` is valid for writes by the safety requirements of this function.
unsafe { slot.write(self) }; unsafe { slot.write(self) };
Ok(()) Ok(())
@ -1401,14 +1401,36 @@ unsafe impl<T, E> Init<T, E> for T {
// SAFETY: the `__pinned_init` function always returns `Ok(())` and initializes every field of // SAFETY: the `__pinned_init` function always returns `Ok(())` and initializes every field of
// `slot`. Additionally, all pinning invariants of `T` are upheld. // `slot`. Additionally, all pinning invariants of `T` are upheld.
unsafe impl<T, E> PinInit<T, E> for T { unsafe impl<T> PinInit<T> for T {
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), Infallible> {
// SAFETY: `slot` is valid for writes by the safety requirements of this function. // SAFETY: `slot` is valid for writes by the safety requirements of this function.
unsafe { slot.write(self) }; unsafe { slot.write(self) };
Ok(()) Ok(())
} }
} }
// SAFETY: when the `__init` function returns with
// - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld.
// - `Err(err)`, slot was not written to.
unsafe impl<T, E> Init<T, E> for Result<T, E> {
unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
// SAFETY: `slot` is valid for writes by the safety requirements of this function.
unsafe { slot.write(self?) };
Ok(())
}
}
// SAFETY: when the `__pinned_init` function returns with
// - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld.
// - `Err(err)`, slot was not written to.
unsafe impl<T, E> PinInit<T, E> for Result<T, E> {
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
// SAFETY: `slot` is valid for writes by the safety requirements of this function.
unsafe { slot.write(self?) };
Ok(())
}
}
/// Smart pointer containing uninitialized memory and that can write a value. /// Smart pointer containing uninitialized memory and that can write a value.
pub trait InPlaceWrite<T> { pub trait InPlaceWrite<T> {
/// The type `Self` turns into when the contents are initialized. /// The type `Self` turns into when the contents are initialized.