Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vec improvements #427

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Added `format` macro.
- Added `String::from_utf16`.
- Added `Vec::spare_capacity_mut`
- Implemented `From<[T; N]>` for Vec

### Changed

Expand Down
61 changes: 61 additions & 0 deletions src/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,36 @@ impl<T, const N: usize> Vec<T, N> {
// All item are processed. This can be optimized to `set_len` by LLVM.
drop(g);
}

/// Returns the remaining spare capacity of the vector as a slice of `MaybeUninit<T>`.
///
/// The returned slice can be used to fill the vector with data before marking the data as
/// initialized using the `set_len` method.
///
/// # Examples
///
/// ```
/// use heapless::Vec;
///
/// // Allocate vector big enough for 10 elements.
/// let mut v: Vec<_, 10> = Vec::new();
///
/// // Fill in the first 3 elements.
/// let uninit = v.spare_capacity_mut();
/// uninit[0].write(0);
/// uninit[1].write(1);
/// uninit[2].write(2);
///
/// // Mark the first 3 elements of the vector as being initialized.
/// unsafe {
/// v.set_len(3);
/// }
///
/// assert_eq!(&v, &[0, 1, 2]);
/// ```
pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>] {
&mut self.buffer[self.len..]
}
}

// Trait implementations
Expand Down Expand Up @@ -858,6 +888,17 @@ impl<T, const N: usize> Drop for Vec<T, N> {
}
}

impl<T, const N: usize> From<[T; N]> for Vec<T, N> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it feels a bit strange that the array length becomes the capacity. Intuitively I'd say it should be possible to convert a [u8; 10] into a Vec<u8; 20> with 10 filled elements.

This would fail if CAP < N, and there's no way to add where CAP >= N in const generics, so tihs'd have to be TryFrom which perhaps is not that ergonomic?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a way to generate a compile error in that case. This PR does this: #352

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought doing it this way would allow me to "move" the array into the Vec without copying, but it turns out that copying will happen either way, so might as well allow N < CAP.

/// Converts array to `Vec` of same size and capacity without copying
fn from(buffer: [T; N]) -> Self {
Self {
// cast [T; N] into [MaybeUninit<T>; N]
buffer: unsafe { buffer.as_ptr().cast::<[MaybeUninit<T>; N]>().read() },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does a bitwise copy of the elements into the Vec, but will still drop them when buffer goes out of scope. This will cause use-after-frees if T is an owned type, like Box.

You can wrap the buffer into a ManuallyDrop to avoid this.

len: N,
}
}
}

impl<'a, T: Clone, const N: usize> TryFrom<&'a [T]> for Vec<T, N> {
type Error = ();

Expand Down Expand Up @@ -1581,4 +1622,24 @@ mod tests {
// Validate full
assert!(v.is_full());
}

#[test]
fn spare_capacity_mut() {
let mut v: Vec<_, 4> = Vec::new();
let uninit = v.spare_capacity_mut();
assert_eq!(uninit.len(), 4);
uninit[0].write(1);
uninit[1].write(2);
uninit[2].write(3);
unsafe { v.set_len(3) };
assert_eq!(v.as_slice(), &[1, 2, 3]);

let uninit = v.spare_capacity_mut();
assert_eq!(uninit.len(), 1);
uninit[0].write(4);
unsafe { v.set_len(4) };
assert_eq!(v.as_slice(), &[1, 2, 3, 4]);

assert!(v.spare_capacity_mut().is_empty());
}
}