rust/kernel/alloc/kvec.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
Add `Vec::into_boxed_slice()` similar to
`std::vec::Vec::into_boxed_slice()` [1].
There is currently no way to easily consume the allocation of a vector.
However, it is very convenient to use `Vec` to initialize a dynamically
sized array and then "seal" it, so it can be passed along as a Box:
fn create_from(src: &[T]) -> Result<KBox<[U]>, AllocError> {
let v = Vec::with_capacity(n, GFP_KERNEL)?;
for i in src {
v.push(foo(i)?, GFP_KERNEL)?;
}
Ok(v.into_boxed_slice())
}
A valid alternative is to use `Box::new_uninit()` rather than
`Vec::with_capacity()`, and eventually convert the box via
`Box::assume_init()`. This works but needlessly requires unsafe code,
awkward drop handling, etc. Using `Vec` is the much simpler solution.
[1] https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_boxed_slice
Signed-off-by: David Rheinsberg <david@readahead.eu>
---
rust/kernel/alloc/kvec.rs | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index ac8d6f763ae8..537818bb66d0 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -826,6 +826,23 @@ pub fn resize(&mut self, new_len: usize, value: T, flags: Flags) -> Result<(), A
}
}
}
+
+ /// Converts the vector into [`Box<[T], A>`].
+ ///
+ /// Excess capacity is retained in the allocation, but lost until the box
+ /// is dropped.
+ pub fn into_boxed_slice(self) -> Box<[T], A> {
+ let (buf, len, _cap) = self.into_raw_parts();
+ let slice = ptr::slice_from_raw_parts_mut(buf, len);
+
+ // SAFETY:
+ // - `slice` has been allocated with `A`
+ // - `slice` is suitably aligned
+ // - `slice` has at least a length of `len`
+ // - all elements within `slice` are initialized values of `T`
+ // - `len` does not exceed `isize::MAX`
+ unsafe { Box::from_raw(slice) }
+ }
}
impl<T, A> Drop for Vec<T, A>
--
2.53.0
On Thu Mar 26, 2026 at 10:56 AM CET, David Rheinsberg wrote:
> Add `Vec::into_boxed_slice()` similar to
> `std::vec::Vec::into_boxed_slice()` [1].
Do you have a user for this?
> + /// Converts the vector into [`Box<[T], A>`].
> + ///
> + /// Excess capacity is retained in the allocation, but lost until the box
> + /// is dropped.
> + pub fn into_boxed_slice(self) -> Box<[T], A> {
> + let (buf, len, _cap) = self.into_raw_parts();
> + let slice = ptr::slice_from_raw_parts_mut(buf, len);
> +
> + // SAFETY:
> + // - `slice` has been allocated with `A`
> + // - `slice` is suitably aligned
> + // - `slice` has at least a length of `len`
> + // - all elements within `slice` are initialized values of `T`
> + // - `len` does not exceed `isize::MAX`
> + unsafe { Box::from_raw(slice) }
Box::from_raw() is missing the safety requirement that the allocation made with
A must have been made for Layout::for_value::<T>(), as this is what we assume in
the destructor.
For this function we should call A::realloc() and document that
into_boxed_slice() may shrink the backing allocation.
Additionally, can you please add a doc-test?
Thanks,
Danilo
Hi
On Thu, Mar 26, 2026, at 11:53 AM, Danilo Krummrich wrote:
> On Thu Mar 26, 2026 at 10:56 AM CET, David Rheinsberg wrote:
>> Add `Vec::into_boxed_slice()` similar to
>> `std::vec::Vec::into_boxed_slice()` [1].
>
> Do you have a user for this?
Yes.
>> + /// Converts the vector into [`Box<[T], A>`].
>> + ///
>> + /// Excess capacity is retained in the allocation, but lost until the box
>> + /// is dropped.
>> + pub fn into_boxed_slice(self) -> Box<[T], A> {
>> + let (buf, len, _cap) = self.into_raw_parts();
>> + let slice = ptr::slice_from_raw_parts_mut(buf, len);
>> +
>> + // SAFETY:
>> + // - `slice` has been allocated with `A`
>> + // - `slice` is suitably aligned
>> + // - `slice` has at least a length of `len`
>> + // - all elements within `slice` are initialized values of `T`
>> + // - `len` does not exceed `isize::MAX`
>> + unsafe { Box::from_raw(slice) }
>
> Box::from_raw() is missing the safety requirement that the allocation made with
> A must have been made for Layout::for_value::<T>(), as this is what we assume in
> the destructor.
Since `slice` is typed, shouldn't this be implied by:
"`slice` has been allocated with `A`"
There is also no mention of it in `From<Box<[T], A>> for Vec<T, A>`. This should have the same requirements, shouldn't it?
I will gladly mention it in v2, though.
> For this function we should call A::realloc() and document that
> into_boxed_slice() may shrink the backing allocation.
Will do.
> Additionally, can you please add a doc-test?
Do you want me to add a functionality test, or do you want me to add an example for documentation purposes? `doc-test` is a bit ambiguous in that regard.
Thanks
David
On Thu Mar 26, 2026 at 1:20 PM CET, David Rheinsberg wrote:
> Yes.
So, which one is it?
> Since `slice` is typed, shouldn't this be implied by:
>
> "`slice` has been allocated with `A`"
Allocator::free() requires
/// - `layout` must match the `Layout` the allocation has been created with.
I don't see how this is implicitly justified here; the allocation could have
been created with a different layout, i.e. it may be that cap != len.
> There is also no mention of it in `From<Box<[T], A>> for Vec<T, A>`. This
> should have the same requirements, shouldn't it?
This is different, as in this case we can guarantee len == cap.
> I will gladly mention it in v2, though.
We should fix the safety requirement of Box::from_raw().
> Do you want me to add a functionality test, or do you want me to add an
> example for documentation purposes? `doc-test` is a bit ambiguous in that
> regard.
I can be both; examples for edge cases for instance serve both purposes. I.e. it
shouldn't be an excessibe functionality test, but more in general one to three
examples including some edge cases is a good balance. For this one a simple
example should be good enough.
On Thu, Mar 26, 2026 at 10:56:20AM +0100, David Rheinsberg wrote:
> Add `Vec::into_boxed_slice()` similar to
> `std::vec::Vec::into_boxed_slice()` [1].
>
> There is currently no way to easily consume the allocation of a vector.
> However, it is very convenient to use `Vec` to initialize a dynamically
> sized array and then "seal" it, so it can be passed along as a Box:
>
> fn create_from(src: &[T]) -> Result<KBox<[U]>, AllocError> {
> let v = Vec::with_capacity(n, GFP_KERNEL)?;
>
> for i in src {
> v.push(foo(i)?, GFP_KERNEL)?;
> }
>
> Ok(v.into_boxed_slice())
> }
>
> A valid alternative is to use `Box::new_uninit()` rather than
> `Vec::with_capacity()`, and eventually convert the box via
> `Box::assume_init()`. This works but needlessly requires unsafe code,
> awkward drop handling, etc. Using `Vec` is the much simpler solution.
>
> [1] https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_boxed_slice
>
> Signed-off-by: David Rheinsberg <david@readahead.eu>
> + /// Excess capacity is retained in the allocation, but lost until the box
> + /// is dropped.
We currently say that you should provide the right length when freeing
an allocation, but this is going to violate that. You should probably
invoke realloc here, like stdlib does.
Alice
Hi Alice,
On Thu, Mar 26, 2026, at 11:33 AM, Alice Ryhl wrote:
> On Thu, Mar 26, 2026 at 10:56:20AM +0100, David Rheinsberg wrote:
>> Add `Vec::into_boxed_slice()` similar to
>> `std::vec::Vec::into_boxed_slice()` [1].
>>
>> There is currently no way to easily consume the allocation of a vector.
>> However, it is very convenient to use `Vec` to initialize a dynamically
>> sized array and then "seal" it, so it can be passed along as a Box:
>>
>> fn create_from(src: &[T]) -> Result<KBox<[U]>, AllocError> {
>> let v = Vec::with_capacity(n, GFP_KERNEL)?;
>>
>> for i in src {
>> v.push(foo(i)?, GFP_KERNEL)?;
>> }
>>
>> Ok(v.into_boxed_slice())
>> }
>>
>> A valid alternative is to use `Box::new_uninit()` rather than
>> `Vec::with_capacity()`, and eventually convert the box via
>> `Box::assume_init()`. This works but needlessly requires unsafe code,
>> awkward drop handling, etc. Using `Vec` is the much simpler solution.
>>
>> [1] https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_boxed_slice
>>
>> Signed-off-by: David Rheinsberg <david@readahead.eu>
>
>> + /// Excess capacity is retained in the allocation, but lost until the box
>> + /// is dropped.
>
> We currently say that you should provide the right length when freeing
> an allocation, but this is going to violate that. You should probably
> invoke realloc here, like stdlib does.
Thanks, didn't know that!
`std` calls `shrink_to_fit()`. Should I add it as well? How about `shrink_to()`? I don't particularly need them, but I can expose them as well, given that I would have to implement those.
Thanks
David
On Thu Mar 26, 2026 at 11:45 AM CET, David Rheinsberg wrote: > On Thu, Mar 26, 2026, at 11:33 AM, Alice Ryhl wrote: >> We currently say that you should provide the right length when freeing >> an allocation, but this is going to violate that. You should probably >> invoke realloc here, like stdlib does. Ok, Alice was faster. :) > `std` calls `shrink_to_fit()`. Should I add it as well? How about > `shrink_to()`? I don't particularly need them, but I can expose them as well, > given that I would have to implement those. Let's stick to A::realloc(), that's the only thing we need to justify the safety requirement. I don't want to rely on shrink_to() and shrink_to_fit() not doing additional things we don't need for this conversion.
© 2016 - 2026 Red Hat, Inc.