heapless icon indicating copy to clipboard operation
heapless copied to clipboard

Infallible Vec operations

Open robin-nitrokey opened this issue 2 years ago • 4 comments

Some operations on a Vec<T, N> are infallible but can only be implemented using fallible methods. What do you think about adding more infallible methods? For example:

  • from_array(data: [T; N]): construct a Vec from an array with the same capacity (would also be useful for constants)
  • fill(&mut self, value: T): resize to capacity and clone value to empty slots
  • fill_default(&mut self): resize to capacity and write T::default() to empty slots
  • replace(&mut self, other: &Self): clear this Vec and copy the data from other

robin-nitrokey avatar Jan 30 '23 20:01 robin-nitrokey

from_array is now implemented in https://github.com/rust-embedded/heapless/pull/352.

fill already exists on slice, i.e. via DerefMut, same with fill_with(Default::default) instead of fill_default. These only fill up to the current length, though. What is the use case for the fill methods filling up to capacity? Is it covered now with .spare_capacity_mut().fill(…)?

For replace, I assume this should work only for Vec with the same capacity, otherwise it would still need to be fallible. Again, what is the use case for this method?

reitermarkus avatar Jan 21 '24 22:01 reitermarkus

Good to see from_array being added! Unfortunately, the implementation is not const. It would still be useful to be able to create non-empty constants.

My use case for fill is preparing a buffer for functions that expect a &mut [u8] for writing. Currently, you have to call v.resize_default(v.capacity()) which is fallible. spare_capacity_mut().fill(MaybeUninit::new(...)) should work but is not really intuitive.

For replace, I don’t remember the specific use case. I’ll report back if I find it again.

robin-nitrokey avatar Jan 22 '24 10:01 robin-nitrokey

@robin-nitrokey, can you give a full example on how fill would be used?

Maybe v.extend(core::iter::repeat(...)) works?

reitermarkus avatar Feb 19 '24 11:02 reitermarkus

I don’t think extend + repeat would work as extend “[p]anics if the vec cannot hold all elements of the iterator.”

Here is a real world example:

https://github.com/trussed-dev/ctap-types/blob/084db87a800e1204bcfdc3e25ca46bc4a81650ba/src/ctap2.rs#L148-L175

serialize receives a &mut Vec<u8, N> buffer and wants to write data to the buffer by calling cbor_serialize, which expects a &mut [u8] buffer. So we need to extend the Vec to capacity, write to it and then truncate it to the written data.

Here, buffer.resize_default(buffer.capacity()).ok() could be replaced with one of:

  • buffer.fill(Default::default())
  • buffer.fill_default()
  • buffer.resize_to_capacity()

robin-nitrokey avatar Feb 19 '24 12:02 robin-nitrokey