Skip to content

Add array_chunks - #1023

Open
ronnodas wants to merge 16 commits into
rust-itertools:masterfrom
ronnodas:array-chunks
Open

Add array_chunks#1023
ronnodas wants to merge 16 commits into
rust-itertools:masterfrom
ronnodas:array-chunks

Conversation

@ronnodas

@ronnodas ronnodas commented Mar 2, 2025

Copy link
Copy Markdown
Contributor

Added an implementation of arrays that just repeatedly calls next_array(). Currently the N = 0 produces a post-monomorphization error, see the discussion in #1012.

The remainder() feature is not implemented, based on the discussion below, since this seems to require either:

  • gating this behind use_alloc, or
  • making the return type not Clone, or
  • more unsafe code implementing effectively more of arrayvec.

@codecov

codecov Bot commented Mar 2, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.91%. Comparing base (6814180) to head (e923eb1).
⚠️ Report is 211 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1023      +/-   ##
==========================================
- Coverage   94.38%   93.91%   -0.48%     
==========================================
  Files          48       53       +5     
  Lines        6665     6707      +42     
==========================================
+ Hits         6291     6299       +8     
- Misses        374      408      +34     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ronnodas

ronnodas commented Mar 2, 2025

Copy link
Copy Markdown
Contributor Author

Re the MSRV failure, I'm not sure what the idiomatic way is to trigger a PME on 1.63 based on N > 0.

@jswrenn

jswrenn commented Mar 3, 2025

Copy link
Copy Markdown
Member

Re the MSRV failure, I'm not sure what the idiomatic way is to trigger a PME on 1.63 based on N > 0.

You can do it like this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=233fb2f6ca3df52d15e3df96b8c4cb61

Comment thread src/arrays.rs Outdated
Comment on lines +88 to +102
/// Effectively assert!(N > 0) post-monomorphization
fn assert_positive<const N: usize>() {
trait StaticAssert<const N: usize> {
const ASSERT: bool;
}

impl<const N: usize> StaticAssert<N> for () {
const ASSERT: bool = {
assert!(N > 0);
true
};
}

assert!(<() as StaticAssert<N>>::ASSERT);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rust doesn't provide stacktraces for monomorphization errors, so folks will only see that an error occured in assert_positive — and not the code that actually required the assertion.

Define this as a macro and call that instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is the change in db21b2f what you meant?

Comment thread src/lib.rs
/// assert_eq!(Some([]), it.next());
/// ```
#[cfg(feature = "use_alloc")]
fn arrays<const N: usize>(self) -> Arrays<Self, N>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IMO array_chunks is the better name, since it differentiates it from array_windows.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To add context, I was trying to avoid triggering the unstable-name-collisions lint, because of rust-lang/rust#100450. Should I change it back to array_chunks?

Comment thread src/arrays.rs
Comment on lines +9 to +12
pub struct Arrays<I: Iterator, const N: usize> {
iter: I,
partial: Vec<I::Item>,
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It strikes me as a code smell that N doesn't appear in the definition here. Shouldn't partial be an ArrayBuilder<T, N>?

@ronnodas ronnodas Mar 5, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Making it ArrayBuilder<T, N> a priori makes sense, but I had trouble with the bounds for the impl Clone in that case. Note that MaybeUninit<T>: Clone requires T: Copy.

Maybe it could also be array::IntoIter<T, N> or a hypothetical ArrayBuilder::IntoIter<T, N> (this is close to what the unstable Iterator::ArrayChunks does) but I think the unsafe code for that would need to be more careful.

The state you need to keep track of doesn't really depend on N (except partial.len() <= N but this is sort of incidental). However Arrays needs to have N as a parameter for Arrays::Item to be well-defined.

Suggestions?

@phimuemue phimuemue Jul 24, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, should be ArrayBuilder, because this brings it closer to no_alloc.

Maybe this array builder could also be re-used in next.

Can you remember/share the exact problem?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@phimuemue If it holds an ArrayBuilder<T> then it won't be Clone without T : Copy. But we should have Arrays<I, N>: Clone when I: Clone and I::Item : Clone.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ok. Then, let's implement it without partial/remaining for now.

(Because if we extend our ArrayBuilder further and further, we land at (a less tested version of) ArrayVec.)

@ronnodas
ronnodas requested a review from jswrenn June 20, 2025 18:47
@king-11

king-11 commented Oct 12, 2025

Copy link
Copy Markdown

what are the thoughts about having an array_chunks which can fill the partially filled last item by fetching from a filler that the caller provides? I think it would be a very useful API in case people always want a certain length chunks

@bend-n

bend-n commented Nov 11, 2025

Copy link
Copy Markdown

im curious why you didnt want to just copy the previous stdlib implementation? you can simply implement this on top of a manual as_chunks?
returning an owned array and using a vec for remainder is kind of funky.

@ronnodas

ronnodas commented Nov 11, 2025

Copy link
Copy Markdown
Contributor Author

@bend-n Do you mean slice::as_chunks? That works for slices, but this is for a general iterator.

There's also a nightly version of this in std: Iterator::array_chunks. If you're asking about the implementation there, I don't think the code in this PR is that different, just trying to reuse things already in the crate. But TBF I haven't looked at either that implementation or this PR closely in a while.

@bend-n

bend-n commented Nov 11, 2025

Copy link
Copy Markdown

oh, my bad. i forgot that iterator::array_chunks was still unstable and assumed this was with regards to slice array chunks for some reason.

you could still copy the implementation of iterator::array_chunks, though?

@ronnodas

Copy link
Copy Markdown
Contributor Author

you could still copy the implementation of iterator::array_chunks, though?

If you go through the source, Iterator::array_chunks is implemented in terms of (also unstable) Iterator::next_chunk, which is equivalent to Itertools::next_array. It made more sense to use the thing that's already in the crate rather than copy over something with duplicate functionality.

Comment thread src/arrays.rs Outdated
@phimuemue

Copy link
Copy Markdown
Member

If it turns out to be too complicated, maybe go without partial in our first iteration?

@ronnodas

Copy link
Copy Markdown
Contributor Author

@phimuemue I've removed partial/remainder() and thereby the gating behind use_alloc.

Comment thread src/next_array.rs Outdated
Comment thread src/arrays.rs Outdated
Comment thread src/arrays.rs Outdated
Comment thread tests/quick.rs Outdated
Comment thread src/lib.rs
/// let it = (1..7).arrays::<3>();
/// itertools::assert_equal(it, vec![[1, 2, 3], [4, 5, 6]]);
///
/// // you can also specify the complete type

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's not encourage people to spell iterator types

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note that such an example is included in the docs for tuple_windows(), tuples(), tuple_combinations() and array_combinations(). Should I still remove this or keep it for consistency?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As you wish.

Comment thread src/lib.rs

/// Return an iterator that groups the items in arrays of const generic size `N`.
///
/// `N == 0` is a compile-time (but post-monomorphization) error.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it really important for users that it is post-monomorphization?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It won't be caught by cargo check, for example, which seems relevant to users.

Comment thread src/arrays.rs
@phimuemue

phimuemue commented Jul 26, 2026

Copy link
Copy Markdown
Member

@jswrenn regarding naming: what's our policy? IMHO, the best name is array_chunks, and I would use it and deprecate our function once it's also available in std.

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.

5 participants