Skip to content

Conversation

nullstalgia
Copy link

Howdy!

As I was working on simplifying a crate's logic to fit in a no_std context, I found that Deque didn't have some of the methods I was using from alloc's implementation.

So I took the time to port them over! Apologies if the comments in truncate are a bit much, it was mostly to help me wrap my head around how the data structure itself used its private variables.

If you spot an error in my logic or if I missed something, please let me know!

Copy link
Contributor

@sgued sgued left a comment

Choose a reason for hiding this comment

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

Looking good.

I'd love to have tests covering all possible branches in the truncate method though.

src/deque.rs Outdated
// Stage 1: Check if all values can be retained.
while cur < len {
// Safety: `cur` must be under the deque's total length as per the loop condition
if !f(unsafe { self.get_unchecked_mut(cur) }) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why deviate from std's implementation and use unsafe here? I expect the bound check introduced in the safe path to be optimized out.

Copy link
Author

Choose a reason for hiding this comment

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

Fair! My reasoning came from the fact that Deque doesn't impl ops::IndexMut (and most things in heapless seem to not, I'm unsure how intentional of a choice that is still). I like to avoid unwraps in unsafe blocks for a few reasons; expect is fine, but I also felt it was unnecessarily explicit? I mean, std::VecDeque's IndexMut impl comes out to be the same as what I have now changed it to, a get_mut() with an except().

So maybe another PR adding those ops impls would be in order? Unless this was an explicit choice to not allow indexing like that, I'm still new to heapless as a whole 😅.

src/deque.rs Outdated
Comment on lines 1890 to 1894
// Based on alloc's own VecDeque test
//
// https://github.com/rust-lang/rust/blob/fa3155a644dd62e865825087b403646be01d4cef/library/alloc/src/collections/vec_deque/tests.rs#L1086
//
// Tests that each element's destructor is called when being truncated.
Copy link
Contributor

Choose a reason for hiding this comment

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

Please test the following cases:

  • Truncating an empty deque
  • Truncating an continuous deque
    • leaving it unchanged
    • leaving it empty
    • leaving it half-empty
  • Truncating a discontinuous deque
    • leaving it unchanged
    • leaving it discontinuous but dropping some elements
    • leaving it continuous (i.e. all the back is dropped)
      • reducing the front slice
      • not touching the front slice
    • leaving it empty

Essentially ensure that all branches of the truncate function are exercized.

Copy link
Author

Choose a reason for hiding this comment

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

Added more tests, thanks for the suggestions! I was surprised there wasn't more in the std, maybe I just didn't look deep enough.

Let me know if any should be adjusted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants