On Mon Aug 17, 2026 at 1:56 PM BST, Eliot Courtney wrote:
> Add `Vec::push_init` which initializes a new element in place. We can't
> modify the existing `Vec::push` signature to take an `impl Init<T, E>`
> without changing its Error type.
>
> Signed-off-by: Eliot Courtney <[email protected]>
> ---
>  rust/kernel/alloc/kvec.rs | 42 +++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 41 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
> index c7546b9da4fa..9f6f25d7e218 100644
> --- a/rust/kernel/alloc/kvec.rs
> +++ b/rust/kernel/alloc/kvec.rs
> @@ -52,7 +52,10 @@
>      }, //
>  };
>  
> -use pin_init::Zeroable;
> +use pin_init::{
> +    Init,
> +    Zeroable, //
> +};
>  
>  mod errors;
>  pub use self::errors::{InsertError, PushError, RemoveError};
> @@ -359,6 +362,43 @@ pub fn push(&mut self, v: T, flags: Flags) -> Result<(), 
> AllocError> {
>          Ok(())
>      }
>  
> +    /// Appends an element to the back of the [`Vec`] instance by 
> initializing it in place.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// struct Element {
> +    ///     buf: KVec<u8>,
> +    /// }
> +    ///
> +    /// impl Element {
> +    ///     fn new() -> impl Init<Self, Error> {
> +    ///         try_init!(Element {
> +    ///             buf: KVec::with_capacity(16, GFP_KERNEL)?,
> +    ///         }? Error)
> +    ///     }
> +    /// }
> +    ///
> +    /// let mut v: KVec<Element> = KVec::new();
> +    /// v.push_init(Element::new(), GFP_KERNEL)?;
> +    /// assert!(v[0].buf.is_empty());
> +    /// # Ok::<(), Error>(())
> +    /// ```
> +    pub fn push_init<E>(&mut self, init: impl Init<T, E>, flags: Flags) -> 
> Result<(), E>
> +    where
> +        E: From<AllocError>,
> +    {
> +        self.reserve(1, flags)?;
> +        // SAFETY: The call to `reserve` was successful, so there is at 
> least one spare slot; the
> +        // pointer therefore refers to allocated, aligned memory valid for a 
> write of one `T`.
> +        unsafe { 
> init.__init(self.spare_capacity_mut().as_mut_ptr().cast::<T>())? };
> +        // SAFETY: The call to `__init` returned `Ok`, so the first spare 
> slot now holds an
> +        // initialized `T`. The new length does not exceed the capacity 
> because `reserve` ensured
> +        // the capacity is greater than the length by at least one.
> +        unsafe { self.inc_len(1) };
> +        Ok(())
> +    }

Thinking about this from a fresh design perspective, I wonder if we can create
something more composable by splitting the allocation and insertion, like entry
APIs do.

So

    impl<T, A: Allocator> Vec<T, A> {
        pub fn reserve(&mut self, additional: usize, flags: Flags) -> 
Result<Reservation<'_, T>, AllocError> {
            ...
        }
    }

    /// Type indicating vector with reserved capacity.
    pub struct<'a> Reservation<'a, T> {
    }

    impl<'a, T> Reservation<'a, T> {
        pub fn init(&mut self, i: impl Init<T, E>) -> Result<(), E> {
            ...
        }
    }

You can imagine even pushing this further, e.g. have a type indicating just a
single reserved slot. Or perhaps have a type that is `Vec` but with fixed
capacity and cannot reallocate (something like `ArrayVec`) that the reserve
method will return.

Best,
Gary

> +
>      /// Appends an element to the back of the [`Vec`] instance without 
> reallocating.
>      ///
>      /// Fails if the vector does not have capacity for the new element.


Reply via email to