Re: [PATCH 1/6] rust: alloc: add Vec::push_init

"Gary Guo" <[email protected]>
Newsgroups org.kernel.vger.rust-for-linux,dev.linux.lists.nova-gpu,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.