Repository: rust-itertools/itertools
Pull request #867 (2024-01-31 22:32:57)

Bump codecov/codecov-action from 3 to 4
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/codecov/codecov-action/releases">codecov/codecov-action's releases</a>.</em></p>
<blockquote>
<h2>v4.0.0</h2>
<p>v4 of the Codecov Action uses the <a href="https://docs.codecov.com/docs/the-codecov-cli">CLI</a> as the underlying upload. The CLI has helped to power new features including local upload, the global upload token, and new upcoming features.</p>
<h2>Breaking Changes</h2>
<ul>
<li>The Codecov Action runs as a <code>node20</code> action due to <code>node16</code> deprecation. See <a href="https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/">this post from GitHub</a> on how to migrate.</li>
<li>Tokenless uploading is unsupported. However, PRs made from forks to the upstream public repos will support tokenless (e.g. contributors to OS projects do not need the upstream repo's Codecov token). This <a href="https://docs.codecov.com/docs/adding-the-codecov-token#github-actions">doc</a> shows instructions on how to add the Codecov token.</li>
<li>OS platforms have been added, though some may not be automatically detected. To see a list of platforms, see our <a href="https://cli.codecov.io">CLI download page</a></li>
<li>Various arguments to the Action have been changed. Please be aware that the arguments match with the CLI's needs</li>
</ul>
<p><code>v3</code> versions and below will not have access to CLI features (e.g. global upload token, ATS).</p>
<h2>What's Changed</h2>
<ul>
<li>build(deps): bump openpgp from 5.8.0 to 5.9.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/985">codecov/codecov-action#985</a></li>
<li>build(deps): bump actions/checkout from 3.0.0 to 3.5.3 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1000">codecov/codecov-action#1000</a></li>
<li>build(deps): bump ossf/scorecard-action from 2.1.3 to 2.2.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1006">codecov/codecov-action#1006</a></li>
<li>build(deps): bump tough-cookie from 4.0.0 to 4.1.3 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1013">codecov/codecov-action#1013</a></li>
<li>build(deps-dev): bump word-wrap from 1.2.3 to 1.2.4 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1024">codecov/codecov-action#1024</a></li>
<li>build(deps): bump node-fetch from 3.3.1 to 3.3.2 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1031">codecov/codecov-action#1031</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 20.1.4 to 20.4.5 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1032">codecov/codecov-action#1032</a></li>
<li>build(deps): bump github/codeql-action from 1.0.26 to 2.21.2 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1033">codecov/codecov-action#1033</a></li>
<li>build commit,report and upload args based on codecovcli by <a href="https://github.com/dana-yaish"><code>@​dana-yaish</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/943">codecov/codecov-action#943</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 20.4.5 to 20.5.3 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1055">codecov/codecov-action#1055</a></li>
<li>build(deps): bump github/codeql-action from 2.21.2 to 2.21.4 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1051">codecov/codecov-action#1051</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 20.5.3 to 20.5.4 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1058">codecov/codecov-action#1058</a></li>
<li>chore(deps): update outdated deps by <a href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1059">codecov/codecov-action#1059</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 20.5.4 to 20.5.6 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1060">codecov/codecov-action#1060</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from 6.4.1 to 6.5.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1065">codecov/codecov-action#1065</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code> from 6.4.1 to 6.5.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1064">codecov/codecov-action#1064</a></li>
<li>build(deps): bump actions/checkout from 3.5.3 to 3.6.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1063">codecov/codecov-action#1063</a></li>
<li>build(deps-dev): bump eslint from 8.47.0 to 8.48.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1061">codecov/codecov-action#1061</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 20.5.6 to 20.5.7 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1062">codecov/codecov-action#1062</a></li>
<li>build(deps): bump openpgp from 5.9.0 to 5.10.1 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1066">codecov/codecov-action#1066</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 20.5.7 to 20.5.9 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1070">codecov/codecov-action#1070</a></li>
<li>build(deps): bump github/codeql-action from 2.21.4 to 2.21.5 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1069">codecov/codecov-action#1069</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code> from 6.5.0 to 6.6.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1072">codecov/codecov-action#1072</a></li>
<li>Update README.md by <a href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1073">codecov/codecov-action#1073</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from 6.5.0 to 6.6.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1071">codecov/codecov-action#1071</a></li>
<li>build(deps-dev): bump <code>@​vercel/ncc</code> from 0.36.1 to 0.38.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1074">codecov/codecov-action#1074</a></li>
<li>build(deps): bump <code>@​actions/core</code> from 1.10.0 to 1.10.1 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1081">codecov/codecov-action#1081</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code> from 6.6.0 to 6.7.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1080">codecov/codecov-action#1080</a></li>
<li>build(deps): bump actions/checkout from 3.6.0 to 4.0.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1078">codecov/codecov-action#1078</a></li>
<li>build(deps): bump actions/upload-artifact from 3.1.2 to 3.1.3 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1077">codecov/codecov-action#1077</a></li>
<li>build(deps-dev): bump <code>@​types/node</code> from 20.5.9 to 20.6.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1075">codecov/codecov-action#1075</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from 6.6.0 to 6.7.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1079">codecov/codecov-action#1079</a></li>
<li>build(deps-dev): bump eslint from 8.48.0 to 8.49.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1076">codecov/codecov-action#1076</a></li>
<li>use cli instead of node uploader by <a href="https://github.com/dana-yaish"><code>@​dana-yaish</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1068">codecov/codecov-action#1068</a></li>
<li>chore(release): 4.0.0-beta.1 by <a href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1084">codecov/codecov-action#1084</a></li>
<li>not adding -n if empty to do-upload command by <a href="https://github.com/dana-yaish"><code>@​dana-yaish</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1085">codecov/codecov-action#1085</a></li>
<li>4.0.0-beta.2 by <a href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1086">codecov/codecov-action#1086</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md">codecov/codecov-action's changelog</a>.</em></p>
<blockquote>
<h2>4.0.0-beta.2</h2>
<h3>Fixes</h3>
<ul>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/1085">#1085</a> not adding -n if empty to do-upload command</li>
</ul>
<h2>4.0.0-beta.1</h2>
<p><code>v4</code> represents a move from the <a href="https://github.com/codecov/uploader">universal uploader</a> to the <a href="https://github.com/codecov/codecov-cli">Codecov CLI</a>. Although this will unlock new features for our users, the CLI is not yet at feature parity with the universal uploader.</p>
<h3>Breaking Changes</h3>
<ul>
<li>No current support for <code>aarch64</code> and <code>alpine</code> architectures.</li>
<li>Tokenless uploading is unsuported</li>
<li>Various arguments to the Action have been removed</li>
</ul>
<h2>3.1.4</h2>
<h3>Fixes</h3>
<ul>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/967">#967</a> Fix typo in README.md</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/971">#971</a> fix: add back in working dir</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/969">#969</a> fix: CLI option names for uploader</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/970">#970</a> build(deps-dev): bump <code>@​types/node</code> from 18.15.12 to 18.16.3</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/979">#979</a> build(deps-dev): bump <code>@​types/node</code> from 20.1.0 to 20.1.2</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/981">#981</a> build(deps-dev): bump <code>@​types/node</code> from 20.1.2 to 20.1.4</li>
</ul>
<h2>3.1.3</h2>
<h3>Fixes</h3>
<ul>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/960">#960</a> fix: allow for aarch64 build</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/957">#957</a> build(deps-dev): bump jest-junit from 15.0.0 to 16.0.0</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/958">#958</a> build(deps): bump openpgp from 5.7.0 to 5.8.0</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/959">#959</a> build(deps-dev): bump <code>@​types/node</code> from 18.15.10 to 18.15.12</li>
</ul>
<h2>3.1.2</h2>
<h3>Fixes</h3>
<ul>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/718">#718</a> Update README.md</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/851">#851</a> Remove unsupported path_to_write_report argument</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/898">#898</a> codeql-analysis.yml</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/901">#901</a> Update README to contain correct information - inputs and negate feature</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/955">#955</a> fix: add in all the extra arguments for uploader</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/819">#819</a> build(deps): bump openpgp from 5.4.0 to 5.5.0</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/835">#835</a> build(deps): bump node-fetch from 3.2.4 to 3.2.10</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/840">#840</a> build(deps): bump ossf/scorecard-action from 1.1.1 to 2.0.4</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/841">#841</a> build(deps): bump <code>@​actions/core</code> from 1.9.1 to 1.10.0</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/843">#843</a> build(deps): bump <code>@​actions/github</code> from 5.0.3 to 5.1.1</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/869">#869</a> build(deps): bump node-fetch from 3.2.10 to 3.3.0</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/872">#872</a> build(deps-dev): bump jest-junit from 13.2.0 to 15.0.0</li>
<li><a href="https://redirect.github.com/codecov/codecov-action/issues/879">#879</a> build(deps): bump decode-uri-component from 0.2.0 to 0.2.2</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/codecov/codecov-action/commit/f30e4959ba63075080d4f7f90cacc18d9f3fafd7"><code>f30e495</code></a> fix: update action.yml (<a href="https://redirect.github.com/codecov/codecov-action/issues/1240">#1240</a>)</li>
<li><a href="https://github.com/codecov/codecov-action/commit/a7b945cea47ad44d8340fae2b004cb982191264f"><code>a7b945c</code></a> fix: allow for other archs (<a href="https://redirect.github.com/codecov/codecov-action/issues/1239">#1239</a>)</li>
<li><a href="https://github.com/codecov/codecov-action/commit/98ab2c591b94478f4c3606d68ff73601df85ec43"><code>98ab2c5</code></a> Update package.json (<a href="https://redirect.github.com/codecov/codecov-action/issues/1238">#1238</a>)</li>
<li><a href="https://github.com/codecov/codecov-action/commit/43235cc5aeeafd8aeb836fe7d647599acead161c"><code>43235cc</code></a> Update README.md (<a href="https://redirect.github.com/codecov/codecov-action/issues/1237">#1237</a>)</li>
<li><a href="https://github.com/codecov/codecov-action/commit/0cf8684c821546a4e0c8c9a4cf4f21a7a0c5014b"><code>0cf8684</code></a> chore(ci): bump to node20 (<a href="https://redirect.github.com/codecov/codecov-action/issues/1236">#1236</a>)</li>
<li><a href="https://github.com/codecov/codecov-action/commit/8e1e730371bf82c744e8ca9aa469e2b7011542ce"><code>8e1e730</code></a> build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code> from 6.19.1 to 6.20.0 ...</li>
<li><a href="https://github.com/codecov/codecov-action/commit/61293af0e8288c75266030376a088c781ec81c18"><code>61293af</code></a> build(deps-dev): bump <code>@​typescript-eslint/parser</code> from 6.19.1 to 6.20.0 (<a href="https://redirect.github.com/codecov/codecov-action/issues/1235">#1235</a>)</li>
<li><a href="https://github.com/codecov/codecov-action/commit/7a070cbe5b1002a522fa2d9446be76dd6abb709c"><code>7a070cb</code></a> build(deps): bump github/codeql-action from 3.23.1 to 3.23.2 (<a href="https://redirect.github.com/codecov/codecov-action/issues/1231">#1231</a>)</li>
<li><a href="https://github.com/codecov/codecov-action/commit/909716592cc1daab655af36c98983bea3bca03e8"><code>9097165</code></a> build(deps): bump actions/upload-artifact from 4.2.0 to 4.3.0 (<a href="https://redirect.github.com/codecov/codecov-action/issues/1232">#1232</a>)</li>
<li><a href="https://github.com/codecov/codecov-action/commit/ac042eaf0f05ddb0e0049e8761603887a080adc3"><code>ac042ea</code></a> build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code> from 6.19.0 to 6.19.1 ...</li>
<li>Additional commits viewable in <a href="https://github.com/codecov/codecov-action/compare/v3...v4">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=codecov/codecov-action&package-manager=github_actions&previous-version=3&new-version=4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

Philippe-Cholet: We may need to get a token: https://docs.codecov.com/docs/adding-the-codecov-token#github-actions

Patch:
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 42c7b30..5c08456 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -29,6 +29,6 @@ jobs:
         run: cargo llvm-cov --all-features --doctests --workspace --lcov --output-path lcov.info
 
       - name: Upload coverage to Codecov
-        uses: codecov/codecov-action@v3
+        uses: codecov/codecov-action@v4
         with:
           files: lcov.info
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..1dbf6f5
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,189 @@
+# Contributing to itertools
+
+We use stable Rust only.
+Please check the minimum version of Rust we use in `Cargo.toml`.
+
+_If you are proposing a major change to CI or a new iterator adaptor for this crate,
+then **please first file an issue** describing your proposal._
+[Usual concerns about new methods](https://github.com/rust-itertools/itertools/issues/413#issuecomment-657670781).
+
+To pass CI tests successfully, your code must be free of "compiler warnings" and "clippy warnings" and be "rustfmt" formatted.
+
+Note that small PRs are easier to review and therefore are more easily merged.
+
+## Write a new method/adaptor for `Itertools` trait
+In general, the code logic should be tested with [quickcheck](https://crates.io/crates/quickcheck) tests in `tests/quick.rs`
+which allow us to test properties about the code with randomly generated inputs.
+
+### Behind `use_std`/`use_alloc` feature?
+If it needs the "std" (such as using hashes) then it should be behind the `use_std` feature,
+or if it requires heap allocation (such as using vectors) then it should be behind the `use_alloc` feature.
+Otherwise it should be able to run in `no_std` context.
+
+This mostly applies to your new module, each import from it, and to your new `Itertools` method.
+
+### Pick the right receiver
+`self`, `&mut self` or `&self`? From [#710](https://github.com/rust-itertools/itertools/pull/710):
+
+- Take by value when:
+    - It transfers ownership to another iterator type, such as `filter`, `map`...
+    - It consumes the iterator completely, such as `count`, `last`, `max`...
+- Mutably borrow when it consumes only part of the iterator, such as `find`, `all`, `try_collect`...
+- Immutably borrow when there is no change, such as `size_hint`.
+
+### Laziness
+Iterators are [lazy](https://doc.rust-lang.org/std/iter/index.html#laziness):
+
+- structs of iterator adaptors should have `#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]` ;
+- structs of iterators should have `#[must_use = "iterators are lazy and do nothing unless consumed"]`.
+
+Those behaviors are **tested** in `tests/laziness.rs`.
+
+## Specialize `Iterator` methods
+It might be more performant to specialize some methods.
+However, each specialization should be thoroughly tested.
+
+Correctly specializing methods can be difficult, and _we do not require that you do it on your initial PR_.
+
+Most of the time, we want specializations of:
+
+- [`size_hint`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.size_hint):
+  It mostly allows allocation optimizations.
+  When always exact, it also enables to implement `ExactSizeIterator`.
+  See our private module `src/size_hint.rs` for helpers.
+- [`fold`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold)
+  might make iteration faster than calling `next` repeatedly.
+- [`count`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.count),
+  [`last`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.last),
+  [`nth`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.nth)
+  as we might be able to avoid iterating on every item with `next`.
+
+Additionally,
+
+- `for_each`, `reduce`, `max/min[_by[_key]]` and `partition` all rely on `fold` so you should specialize it instead.
+- `all`, `any`, `find`, `find_map`, `cmp`, `partial_cmp`, `eq`, `ne`, `lt`, `le`, `gt`, `ge` and `position` all rely (by default) on `try_fold`
+  which we can not specialize on stable rust, so you might want to wait it stabilizes
+  or specialize each of them.
+- `DoubleEndedIterator::{nth_back, rfold, rfind}`: similar reasoning.
+
+An adaptor might use the inner iterator specializations for its own specializations.
+
+They are **tested** in `tests/specializations.rs` and **benchmarked** in `benches/specializations.rs`
+(build those benchmarks is slow so you might want to temporarily remove the ones you do not want to measure).
+
+## Additional implementations
+### The [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) implementation
+All our iterators should implement `Debug`.
+
+When one of the field is not debuggable (such as _functions_), you must not derive `Debug`.
+Instead, manually implement it and _ignore this field_ in our helper macro `debug_fmt_fields`.
+
+<details>
+<summary>4 examples (click to expand)</summary>
+
+```rust
+use std::fmt;
+
+/* ===== Simple derive. ===== */
+#[derive(Debug)]
+struct Name1<I> {
+    iter: I,
+}
+
+/* ===== With an unclonable field. ===== */
+struct Name2<I, F> {
+    iter: I,
+    func: F,
+}
+
+// No `F: Debug` bound and the field `func` is ignored.
+impl<I: fmt::Debug, F> fmt::Debug for Name2<I, F> {
+    // it defines the `fmt` function from a struct name and the fields you want to debug.
+    debug_fmt_fields!(Name2, iter);
+}
+
+/* ===== With an unclonable field, but another bound to add. ===== */
+struct Name3<I: Iterator, F> {
+    iter: I,
+    item: Option<I::Item>,
+    func: F,
+}
+
+// Same about `F` and `func`, similar about `I` but we must add the `I::Item: Debug` bound.
+impl<I: Iterator + fmt::Debug, F> fmt::Debug for Name3<I, F>
+where
+    I::Item: fmt::Debug,
+{
+    debug_fmt_fields!(Name3, iter, item);
+}
+
+/* ===== With an unclonable field for which we can provide some information. ===== */
+struct Name4<I, F> {
+    iter: I,
+    func: Option<F>,
+}
+
+// If ignore a field is not good enough, implement Debug fully manually.
+impl<I: fmt::Debug, F> fmt::Debug for Name4<I, F> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let func = if self.func.is_some() { "Some(_)" } else { "None" };
+        f.debug_struct("Name4")
+            .field("iter", &self.iter)
+            .field("func", &func)
+            .finish()
+    }
+}
+```
+</details>
+
+### When/How to implement [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html)
+All our iterators should implement `Clone` when possible.
+
+Note that a mutable reference is never clonable so `struct Name<'a, I: 'a> { iter: &'a mut I }` can not implement `Clone`.
+
+Derive `Clone` on a generic struct adds the bound `Clone` on each generic parameter.
+It might be an issue in which case you should manually implement it with our helper macro `clone_fields` (it defines the `clone` function calling `clone` on each field) and be careful about the bounds.
+
+### When to implement [`std::iter::FusedIterator`](https://doc.rust-lang.org/std/iter/trait.FusedIterator.html)
+This trait should be implemented _by all iterators that always return `None` after returning `None` once_, because it allows to optimize `Iterator::fuse()`.
+
+The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, eventually refined to ensure it behaves in a fused way.
+
+### When to implement [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html)
+_When we are always able to return an exact non-overflowing length._
+
+Therefore, we do not implement it on adaptors that makes the iterator longer as the resulting length could overflow.
+
+One should not override `ExactSizeIterator::len` method but rely on an exact `Iterator::size_hint` implementation, meaning it returns `(length, Some(length))` (unless you could make `len` more performant than the default).
+
+The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure the size hint is exact.
+
+### When to implement [`DoubleEndedIterator`](https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html)
+When the iterator structure allows to handle _iterating on both fronts simultaneously_.
+The iteration might stop in the middle when both fronts meet.
+
+The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure we can iterate on both fronts simultaneously.
+
+### When to implement [`itertools::PeekingNext`](https://docs.rs/itertools/latest/itertools/trait.PeekingNext.html)
+TODO
+
+This is currently **tested** in `tests/test_std.rs`.
+
+## About lending iterators
+TODO
+
+
+## Other notes
+No guideline about using `#[inline]` yet.
+
+### `.fold` / `.for_each` / `.try_fold` / `.try_for_each`
+In the Rust standard library, it's quite common for `fold` to be implemented in terms of `try_fold`. But it's not something we do yet because we can not specialize `try_fold` methods yet (it uses the unstable `Try`).
+
+From [#781](https://github.com/rust-itertools/itertools/pull/781), the general rule to follow is something like this:
+
+- If you need to completely consume an iterator:
+  - Use `fold` if you need an _owned_ access to an accumulator.
+  - Use `for_each` otherwise.
+- If you need to partly consume an iterator, the same applies with `try_` versions:
+  - Use `try_fold` if you need an _owned_ access to an accumulator.
+  - Use `try_for_each` otherwise.
diff --git a/README.md b/README.md
index 381a3e9..9acac2d 100644
--- a/README.md
+++ b/README.md
@@ -21,17 +21,9 @@ use itertools::Itertools;
 ```
 
 ## How to contribute
+If you're not sure what to work on, try checking the [help wanted](https://github.com/rust-itertools/itertools/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label.
 
-- Fix a bug or implement a new thing
-- Include tests for your new feature, preferably a QuickCheck test
-- Make a Pull Request
-
-For new features, please first consider filing a PR to [rust-lang/rust](https://github.com/rust-lang/rust),
-adding your new feature to the `Iterator` trait of the standard library, if you believe it is reasonable.
-If it isn't accepted there, proposing it for inclusion in ``itertools`` is a good idea.
-The reason for doing is this is so that we avoid future breakage as with ``.flatten()``.
-However, if your feature involves heap allocation, such as storing elements in a ``Vec<T>``,
-then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead.
+See our [CONTRIBUTING.md](https://github.com/rust-itertools/itertools/blob/master/CONTRIBUTING.md) for a detailed guide.
 
 ## License
 
diff --git a/benches/bench1.rs b/benches/bench1.rs
index 8d88816..f718ce2 100644
--- a/benches/bench1.rs
+++ b/benches/bench1.rs
@@ -390,7 +390,7 @@ fn zip_unchecked_counted_loop3(c: &mut Criterion) {
     });
 }
 
-fn group_by_lazy_1(c: &mut Criterion) {
+fn chunk_by_lazy_1(c: &mut Criterion) {
     let mut data = vec![0; 1024];
     for (index, elt) in data.iter_mut().enumerate() {
         *elt = index / 10;
@@ -398,10 +398,10 @@ fn group_by_lazy_1(c: &mut Criterion) {
 
     let data = black_box(data);
 
-    c.bench_function("group by lazy 1", move |b| {
+    c.bench_function("chunk by lazy 1", move |b| {
         b.iter(|| {
-            for (_key, group) in &data.iter().group_by(|elt| **elt) {
-                for elt in group {
+            for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -409,7 +409,7 @@ fn group_by_lazy_1(c: &mut Criterion) {
     });
 }
 
-fn group_by_lazy_2(c: &mut Criterion) {
+fn chunk_by_lazy_2(c: &mut Criterion) {
     let mut data = vec![0; 1024];
     for (index, elt) in data.iter_mut().enumerate() {
         *elt = index / 2;
@@ -417,10 +417,10 @@ fn group_by_lazy_2(c: &mut Criterion) {
 
     let data = black_box(data);
 
-    c.bench_function("group by lazy 2", move |b| {
+    c.bench_function("chunk by lazy 2", move |b| {
         b.iter(|| {
-            for (_key, group) in &data.iter().group_by(|elt| **elt) {
-                for elt in group {
+            for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -436,8 +436,8 @@ fn slice_chunks(c: &mut Criterion) {
 
     c.bench_function("slice chunks", move |b| {
         b.iter(|| {
-            for group in data.chunks(sz) {
-                for elt in group {
+            for chunk in data.chunks(sz) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -453,8 +453,8 @@ fn chunks_lazy_1(c: &mut Criterion) {
 
     c.bench_function("chunks lazy 1", move |b| {
         b.iter(|| {
-            for group in &data.iter().chunks(sz) {
-                for elt in group {
+            for chunk in &data.iter().chunks(sz) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -813,8 +813,8 @@ criterion_group!(
     zipdot_i32_unchecked_counted_loop,
     zipdot_f32_unchecked_counted_loop,
     zip_unchecked_counted_loop3,
-    group_by_lazy_1,
-    group_by_lazy_2,
+    chunk_by_lazy_1,
+    chunk_by_lazy_2,
     slice_chunks,
     chunks_lazy_1,
     equal,
diff --git a/examples/iris.rs b/examples/iris.rs
index 5f55251..a041b28 100644
--- a/examples/iris.rs
+++ b/examples/iris.rs
@@ -77,15 +77,15 @@ fn main() {
     let mut plot_symbols = "+ox".chars().cycle();
     let mut symbolmap = HashMap::new();
 
-    // using Itertools::group_by
-    for (species, species_group) in &irises.iter().group_by(|iris| &iris.name) {
+    // using Itertools::chunk_by
+    for (species, species_chunk) in &irises.iter().chunk_by(|iris| &iris.name) {
         // assign a plot symbol
         symbolmap
             .entry(species)
             .or_insert_with(|| plot_symbols.next().unwrap());
         println!("{} (symbol={})", species, symbolmap[species]);
 
-        for iris in species_group {
+        for iris in species_chunk {
             // using Itertools::format for lazy formatting
             println!("{:>3.1}", iris.data.iter().format(", "));
         }
diff --git a/src/groupbylazy.rs b/src/groupbylazy.rs
index c318ec7..007fea6 100644
--- a/src/groupbylazy.rs
+++ b/src/groupbylazy.rs
@@ -1,7 +1,7 @@
 use alloc::vec::{self, Vec};
 use std::cell::{Cell, RefCell};
 
-/// A trait to unify `FnMut` for `GroupBy` with the chunk key in `IntoChunks`
+/// A trait to unify `FnMut` for `ChunkBy` with the chunk key in `IntoChunks`
 trait KeyFunction<A> {
     type Key;
     fn call_mut(&mut self, arg: A) -> Self::Key;
@@ -282,10 +282,14 @@ where
     }
 }
 
-/// `GroupBy` is the storage for the lazy grouping operation.
+#[deprecated(note = "Use `ChunkBy` instead", since = "0.13.0")]
+/// See [`ChunkBy`](crate::structs::ChunkBy).
+pub type GroupBy<K, I, F> = ChunkBy<K, I, F>;
+
+/// `ChunkBy` is the storage for the lazy grouping operation.
 ///
 /// If the groups are consumed in their original order, or if each
-/// group is dropped without keeping it around, then `GroupBy` uses
+/// group is dropped without keeping it around, then `ChunkBy` uses
 /// no allocations. It needs allocations only if several group iterators
 /// are alive at the same time.
 ///
@@ -294,9 +298,9 @@ where
 /// value. It should be stored in a local variable or temporary and
 /// iterated.
 ///
-/// See [`.group_by()`](crate::Itertools::group_by) for more information.
+/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct GroupBy<K, I, F>
+pub struct ChunkBy<K, I, F>
 where
     I: Iterator,
 {
@@ -307,12 +311,12 @@ where
 }
 
 /// Create a new
-pub fn new<K, J, F>(iter: J, f: F) -> GroupBy<K, J::IntoIter, F>
+pub fn new<K, J, F>(iter: J, f: F) -> ChunkBy<K, J::IntoIter, F>
 where
     J: IntoIterator,
     F: FnMut(&J::Item) -> K,
 {
-    GroupBy {
+    ChunkBy {
         inner: RefCell::new(GroupInner {
             key: f,
             iter: iter.into_iter(),
@@ -329,7 +333,7 @@ where
     }
 }
 
-impl<K, I, F> GroupBy<K, I, F>
+impl<K, I, F> ChunkBy<K, I, F>
 where
     I: Iterator,
 {
@@ -348,7 +352,7 @@ where
     }
 }
 
-impl<'a, K, I, F> IntoIterator for &'a GroupBy<K, I, F>
+impl<'a, K, I, F> IntoIterator for &'a ChunkBy<K, I, F>
 where
     I: Iterator,
     I::Item: 'a,
@@ -368,7 +372,7 @@ where
 /// Iterator element type is `(K, Group)`:
 /// the group's key `K` and the group's iterator.
 ///
-/// See [`.group_by()`](crate::Itertools::group_by) for more information.
+/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct Groups<'a, K, I, F>
 where
@@ -377,7 +381,7 @@ where
     K: 'a,
     F: 'a,
 {
-    parent: &'a GroupBy<K, I, F>,
+    parent: &'a ChunkBy<K, I, F>,
 }
 
 impl<'a, K, I, F> Iterator for Groups<'a, K, I, F>
@@ -418,7 +422,7 @@ where
     K: 'a,
     F: 'a,
 {
-    parent: &'a GroupBy<K, I, F>,
+    parent: &'a ChunkBy<K, I, F>,
     index: usize,
     first: Option<I::Item>,
 }
@@ -476,7 +480,7 @@ where
 
 /// `ChunkLazy` is the storage for a lazy chunking operation.
 ///
-/// `IntoChunks` behaves just like `GroupBy`: it is iterable, and
+/// `IntoChunks` behaves just like `ChunkBy`: it is iterable, and
 /// it only buffers if several chunk iterators are alive at the same time.
 ///
 /// This type implements [`IntoIterator`] (it is **not** an iterator
diff --git a/src/lib.rs b/src/lib.rs
index 1f39379..218b3a8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -37,7 +37,7 @@
 //! - `use_std`
 //!   - Enabled by default.
 //!   - Disable to compile itertools using `#![no_std]`. This disables
-//!     any items that depend on collections (like `group_by`, `unique`,
+//!     any items that depend on collections (like `chunk_by`, `unique`,
 //!     `kmerge`, `join` and many more).
 //!
 //! ## Rust Version
@@ -99,8 +99,11 @@ pub mod structs {
     pub use crate::exactly_one_err::ExactlyOneError;
     pub use crate::flatten_ok::FlattenOk;
     pub use crate::format::{Format, FormatWith};
+    #[allow(deprecated)]
+    #[cfg(feature = "use_alloc")]
+    pub use crate::groupbylazy::GroupBy;
     #[cfg(feature = "use_alloc")]
-    pub use crate::groupbylazy::{Chunk, Chunks, Group, GroupBy, Groups, IntoChunks};
+    pub use crate::groupbylazy::{Chunk, ChunkBy, Chunks, Group, Groups, IntoChunks};
     #[cfg(feature = "use_std")]
     pub use crate::grouping_map::{GroupingMap, GroupingMapBy};
     pub use crate::intersperse::{Intersperse, IntersperseWith};
@@ -249,13 +252,19 @@ macro_rules! iproduct {
     (@flatten $I:expr, $J:expr, $($K:expr,)*) => (
         $crate::iproduct!(@flatten $crate::cons_tuples($crate::iproduct!($I, $J)), $($K,)*)
     );
-    ($I:expr) => (
-        $crate::__std_iter::IntoIterator::into_iter($I)
+    () => (
+        $crate::__std_iter::once(())
+    );
+    ($I:expr $(,)?) => (
+        $crate::__std_iter::IntoIterator::into_iter($I).map(|elt| (elt,))
     );
-    ($I:expr, $J:expr) => (
-        $crate::Itertools::cartesian_product($crate::iproduct!($I), $crate::iproduct!($J))
+    ($I:expr, $J:expr $(,)?) => (
+        $crate::Itertools::cartesian_product(
+            $crate::__std_iter::IntoIterator::into_iter($I),
+            $crate::__std_iter::IntoIterator::into_iter($J),
+        )
     );
-    ($I:expr, $J:expr, $($K:expr),+) => (
+    ($I:expr, $J:expr, $($K:expr),+ $(,)?) => (
         $crate::iproduct!(@flatten $crate::iproduct!($I, $J), $($K,)+)
     );
 }
@@ -575,10 +584,10 @@ pub trait Itertools: Iterator {
     /// Consecutive elements that map to the same key (“runs”), are assigned
     /// to the same group.
     ///
-    /// `GroupBy` is the storage for the lazy grouping operation.
+    /// `ChunkBy` is the storage for the lazy grouping operation.
     ///
     /// If the groups are consumed in order, or if each group's iterator is
-    /// dropped without keeping it around, then `GroupBy` uses no
+    /// dropped without keeping it around, then `ChunkBy` uses no
     /// allocations.  It needs allocations only if several group iterators
     /// are alive at the same time.
     ///
@@ -593,20 +602,20 @@ pub trait Itertools: Iterator {
     /// ```
     /// use itertools::Itertools;
     ///
-    /// // group data into runs of larger than zero or not.
+    /// // chunk data into runs of larger than zero or not.
     /// let data = vec![1, 3, -2, -2, 1, 0, 1, 2];
-    /// // groups:     |---->|------>|--------->|
+    /// // chunks:     |---->|------>|--------->|
     ///
-    /// // Note: The `&` is significant here, `GroupBy` is iterable
+    /// // Note: The `&` is significant here, `ChunkBy` is iterable
     /// // only by reference. You can also call `.into_iter()` explicitly.
     /// let mut data_grouped = Vec::new();
-    /// for (key, group) in &data.into_iter().group_by(|elt| *elt >= 0) {
-    ///     data_grouped.push((key, group.collect()));
+    /// for (key, chunk) in &data.into_iter().chunk_by(|elt| *elt >= 0) {
+    ///     data_grouped.push((key, chunk.collect()));
     /// }
     /// assert_eq!(data_grouped, vec![(true, vec![1, 3]), (false, vec![-2, -2]), (true, vec![1, 0, 1, 2])]);
     /// ```
     #[cfg(feature = "use_alloc")]
-    fn group_by<K, F>(self, key: F) -> GroupBy<K, Self, F>
+    fn chunk_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
     where
         Self: Sized,
         F: FnMut(&Self::Item) -> K,
@@ -615,13 +624,25 @@ pub trait Itertools: Iterator {
         groupbylazy::new(self, key)
     }
 
+    /// See [`.chunk_by()`](Itertools::chunk_by).
+    #[deprecated(note = "Use .chunk_by() instead", since = "0.13.0")]
+    #[cfg(feature = "use_alloc")]
+    fn group_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: PartialEq,
+    {
+        self.chunk_by(key)
+    }
+
     /// Return an *iterable* that can chunk the iterator.
     ///
     /// Yield subiterators (chunks) that each yield a fixed number elements,
     /// determined by `size`. The last chunk will be shorter if there aren't
     /// enough elements.
     ///
-    /// `IntoChunks` is based on `GroupBy`: it is iterable (implements
+    /// `IntoChunks` is based on `ChunkBy`: it is iterable (implements
     /// `IntoIterator`, **not** `Iterator`), and it only buffers if several
     /// chunk iterators are alive at the same time.
     ///


Test patch:
diff --git a/tests/laziness.rs b/tests/laziness.rs
new file mode 100644
index 0000000..cf0c75b
--- /dev/null
+++ b/tests/laziness.rs
@@ -0,0 +1,274 @@
+#![allow(unstable_name_collisions)]
+
+use itertools::Itertools;
+
+#[derive(Debug, Clone)]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+struct Panicking;
+
+impl Iterator for Panicking {
+    type Item = u8;
+
+    fn next(&mut self) -> Option<u8> {
+        panic!("iterator adaptor is not lazy")
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, Some(0))
+    }
+}
+
+impl ExactSizeIterator for Panicking {}
+
+/// ## Usage example
+/// ```compile_fail
+/// must_use_tests! {
+///     name {
+///         Panicking.name(); // Add `let _ =` only if required (encountered error).
+///     }
+///     // ...
+/// }
+/// ```
+///
+/// **TODO:** test missing `must_use` attributes better, maybe with a new lint.
+macro_rules! must_use_tests {
+    ($($(#[$attr:meta])* $name:ident $body:block)*) => {
+        $(
+            /// `#[deny(unused_must_use)]` should force us to ignore the resulting iterators
+            /// by adding `let _ = ...;` on every iterator.
+            /// If it does not, then a `must_use` attribute is missing on the associated struct.
+            ///
+            /// However, it's only helpful if we don't add `let _ =` before seeing if there is an error or not.
+            /// And it does not protect us against removed `must_use` attributes.
+            /// There is no simple way to test this yet.
+            #[deny(unused_must_use)]
+            #[test]
+            $(#[$attr])*
+            fn $name() $body
+        )*
+    };
+}
+
+must_use_tests! {
+    // Itertools trait:
+    interleave {
+        let _ = Panicking.interleave(Panicking);
+    }
+    interleave_shortest {
+        let _ = Panicking.interleave_shortest(Panicking);
+    }
+    intersperse {
+        let _ = Panicking.intersperse(0);
+    }
+    intersperse_with {
+        let _ = Panicking.intersperse_with(|| 0);
+    }
+    zip_longest {
+        let _ = Panicking.zip_longest(Panicking);
+    }
+    zip_eq {
+        let _ = Panicking.zip_eq(Panicking);
+    }
+    batching {
+        let _ = Panicking.batching(Iterator::next);
+    }
+    chunk_by {
+        // ChunkBy
+        let _ = Panicking.chunk_by(|x| *x);
+        // Groups
+        let _ = Panicking.chunk_by(|x| *x).into_iter();
+    }
+    chunks {
+        // IntoChunks
+        let _ = Panicking.chunks(1);
+        let _ = Panicking.chunks(2);
+        // Chunks
+        let _ = Panicking.chunks(1).into_iter();
+        let _ = Panicking.chunks(2).into_iter();
+    }
+    tuple_windows {
+        let _ = Panicking.tuple_windows::<(_,)>();
+        let _ = Panicking.tuple_windows::<(_, _)>();
+        let _ = Panicking.tuple_windows::<(_, _, _)>();
+    }
+    circular_tuple_windows {
+        let _ = Panicking.circular_tuple_windows::<(_,)>();
+        let _ = Panicking.circular_tuple_windows::<(_, _)>();
+        let _ = Panicking.circular_tuple_windows::<(_, _, _)>();
+    }
+    tuples {
+        let _ = Panicking.tuples::<(_,)>();
+        let _ = Panicking.tuples::<(_, _)>();
+        let _ = Panicking.tuples::<(_, _, _)>();
+    }
+    tee {
+        let _ = Panicking.tee();
+    }
+    #[allow(deprecated)]
+    step {
+        let _ = Panicking.step(2);
+    }
+    map_into {
+        let _ = Panicking.map_into::<u16>();
+    }
+    map_ok {
+        let _ = Panicking.map(Ok::<u8, ()>).map_ok(|x| x + 1);
+    }
+    filter_ok {
+        let _ = Panicking.map(Ok::<u8, ()>).filter_ok(|x| x % 2 == 0);
+    }
+    filter_map_ok {
+        let _ = Panicking.map(Ok::<u8, ()>).filter_map_ok(|x| {
+            if x % 2 == 0 {
+                Some(x + 1)
+            } else {
+                None
+            }
+        });
+    }
+    flatten_ok {
+        let _ = Panicking.map(|x| Ok::<_, ()>([x])).flatten_ok();
+    }
+    merge {
+        let _ = Panicking.merge(Panicking);
+    }
+    merge_by {
+        let _ = Panicking.merge_by(Panicking, |_, _| true);
+    }
+    merge_join_by {
+        let _ = Panicking.merge_join_by(Panicking, |_, _| true);
+        let _ = Panicking.merge_join_by(Panicking, Ord::cmp);
+    }
+    #[ignore]
+    kmerge {
+        let _ = Panicking.map(|_| Panicking).kmerge();
+    }
+    #[ignore]
+    kmerge_by {
+        let _ = Panicking.map(|_| Panicking).kmerge_by(|_, _| true);
+    }
+    cartesian_product {
+        let _ = Panicking.cartesian_product(Panicking);
+    }
+    multi_cartesian_product {
+        let _ = vec![Panicking, Panicking, Panicking].into_iter().multi_cartesian_product();
+    }
+    coalesce {
+        let _ = Panicking.coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) });
+    }
+    dedup {
+        let _ = Panicking.dedup();
+    }
+    dedup_by {
+        let _ = Panicking.dedup_by(|_, _| true);
+    }
+    dedup_with_count {
+        let _ = Panicking.dedup_with_count();
+    }
+    dedup_by_with_count {
+        let _ = Panicking.dedup_by_with_count(|_, _| true);
+    }
+    duplicates {
+        let _ = Panicking.duplicates();
+    }
+    duplicates_by {
+        let _ = Panicking.duplicates_by(|x| *x);
+    }
+    unique {
+        let _ = Panicking.unique();
+    }
+    unique_by {
+        let _ = Panicking.unique_by(|x| *x);
+    }
+    peeking_take_while {
+        let _ = Panicking.peekable().peeking_take_while(|x| x % 2 == 0);
+    }
+    take_while_ref {
+        let _ = Panicking.take_while_ref(|x| x % 2 == 0);
+    }
+    take_while_inclusive {
+        let _ = Panicking.take_while_inclusive(|x| x % 2 == 0);
+    }
+    while_some {
+        let _ = Panicking.map(Some).while_some();
+    }
+    #[ignore]
+    tuple_combinations {
+        let _ = Panicking.tuple_combinations::<(_,)>();
+        let _ = Panicking.tuple_combinations::<(_, _)>();
+        let _ = Panicking.tuple_combinations::<(_, _, _)>();
+    }
+    combinations {
+        let _ = Panicking.combinations(0);
+        let _ = Panicking.combinations(1);
+        let _ = Panicking.combinations(2);
+    }
+    combinations_with_replacement {
+        let _ = Panicking.combinations_with_replacement(0);
+        let _ = Panicking.combinations_with_replacement(1);
+        let _ = Panicking.combinations_with_replacement(2);
+    }
+    permutations {
+        let _ = Panicking.permutations(0);
+        let _ = Panicking.permutations(1);
+        let _ = Panicking.permutations(2);
+    }
+    powerset {
+        let _ = Panicking.powerset();
+    }
+    pad_using {
+        let _ = Panicking.pad_using(25, |_| 10);
+    }
+    with_position {
+        let _ = Panicking.with_position();
+    }
+    positions {
+        let _ = Panicking.positions(|v| v % 2 == 0);
+    }
+    update {
+        let _ = Panicking.update(|n| *n += 1);
+    }
+    multipeek {
+        let _ = Panicking.multipeek();
+    }
+    // Not iterator themselves but still lazy.
+    into_grouping_map {
+        let _ = Panicking.map(|x| (x, x + 1)).into_grouping_map();
+    }
+    into_grouping_map_by {
+        let _ = Panicking.into_grouping_map_by(|x| *x);
+    }
+    // Macros:
+    iproduct {
+        let _ = itertools::iproduct!(Panicking);
+        let _ = itertools::iproduct!(Panicking, Panicking);
+        let _ = itertools::iproduct!(Panicking, Panicking, Panicking);
+    }
+    izip {
+        let _ = itertools::izip!(Panicking);
+        let _ = itertools::izip!(Panicking, Panicking);
+        let _ = itertools::izip!(Panicking, Panicking, Panicking);
+    }
+    chain {
+        let _ = itertools::chain!(Panicking);
+        let _ = itertools::chain!(Panicking, Panicking);
+        let _ = itertools::chain!(Panicking, Panicking, Panicking);
+    }
+    // Free functions:
+    multizip {
+        let _ = itertools::multizip((Panicking, Panicking));
+    }
+    put_back {
+        let _ = itertools::put_back(Panicking);
+        let _ = itertools::put_back(Panicking).with_value(15);
+    }
+    peek_nth {
+        let _ = itertools::peek_nth(Panicking);
+    }
+    put_back_n {
+        let _ = itertools::put_back_n(Panicking);
+    }
+    rciter {
+        let _ = itertools::rciter(Panicking);
+    }
+}
diff --git a/tests/macros_hygiene.rs b/tests/macros_hygiene.rs
index d111124..20b59fb 100644
--- a/tests/macros_hygiene.rs
+++ b/tests/macros_hygiene.rs
@@ -1,5 +1,6 @@
 #[test]
 fn iproduct_hygiene() {
+    let _ = itertools::iproduct!();
     let _ = itertools::iproduct!(0..6);
     let _ = itertools::iproduct!(0..6, 0..9);
     let _ = itertools::iproduct!(0..6, 0..9, 0..12);
diff --git a/tests/quick.rs b/tests/quick.rs
index 954655a..f96418f 100644
--- a/tests/quick.rs
+++ b/tests/quick.rs
@@ -1053,58 +1053,58 @@ quickcheck! {
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_1(it: Iter<u8>) -> bool {
+    fn fuzz_chunk_by_lazy_1(it: Iter<u8>) -> bool {
         let jt = it.clone();
-        let groups = it.group_by(|k| *k);
-        itertools::equal(jt, groups.into_iter().flat_map(|(_, x)| x))
+        let chunks = it.chunk_by(|k| *k);
+        itertools::equal(jt, chunks.into_iter().flat_map(|(_, x)| x))
     }
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_2(data: Vec<u8>) -> bool {
-        let groups = data.iter().group_by(|k| *k / 10);
-        let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
+    fn fuzz_chunk_by_lazy_2(data: Vec<u8>) -> bool {
+        let chunks = data.iter().chunk_by(|k| *k / 10);
+        let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
         res
     }
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_3(data: Vec<u8>) -> bool {
-        let grouper = data.iter().group_by(|k| *k / 10);
-        let groups = grouper.into_iter().collect_vec();
-        let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
+    fn fuzz_chunk_by_lazy_3(data: Vec<u8>) -> bool {
+        let grouper = data.iter().chunk_by(|k| *k / 10);
+        let chunks = grouper.into_iter().collect_vec();
+        let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
         res
     }
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
-        let grouper = data.iter().group_by(|k| *k / 3);
-        let mut groups1 = grouper.into_iter();
-        let mut groups2 = grouper.into_iter();
+    fn fuzz_chunk_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
+        let grouper = data.iter().chunk_by(|k| *k / 3);
+        let mut chunks1 = grouper.into_iter();
+        let mut chunks2 = grouper.into_iter();
         let mut elts = Vec::<&u8>::new();
-        let mut old_groups = Vec::new();
+        let mut old_chunks = Vec::new();
 
         let tup1 = |(_, b)| b;
         for &(ord, consume_now) in &order {
-            let iter = &mut [&mut groups1, &mut groups2][ord as usize];
+            let iter = &mut [&mut chunks1, &mut chunks2][ord as usize];
             match iter.next() {
                 Some((_, gr)) => if consume_now {
-                    for og in old_groups.drain(..) {
+                    for og in old_chunks.drain(..) {
                         elts.extend(og);
                     }
                     elts.extend(gr);
                 } else {
-                    old_groups.push(gr);
+                    old_chunks.push(gr);
                 },
                 None => break,
             }
         }
-        for og in old_groups.drain(..) {
+        for og in old_chunks.drain(..) {
             elts.extend(og);
         }
-        for gr in groups1.map(&tup1) { elts.extend(gr); }
-        for gr in groups2.map(&tup1) { elts.extend(gr); }
+        for gr in chunks1.map(&tup1) { elts.extend(gr); }
+        for gr in chunks2.map(&tup1) { elts.extend(gr); }
         itertools::assert_equal(&data, elts);
         true
     }
diff --git a/tests/test_core.rs b/tests/test_core.rs
index 45a8df4..db77a1e 100644
--- a/tests/test_core.rs
+++ b/tests/test_core.rs
@@ -18,6 +18,23 @@ use crate::it::Itertools;
 use core::iter;
 use itertools as it;
 
+#[test]
+fn product0() {
+    let mut prod = iproduct!();
+    assert_eq!(prod.next(), Some(()));
+    assert!(prod.next().is_none());
+}
+
+#[test]
+fn iproduct1() {
+    let s = "αβ";
+
+    let mut prod = iproduct!(s.chars());
+    assert_eq!(prod.next(), Some(('α',)));
+    assert_eq!(prod.next(), Some(('β',)));
+    assert!(prod.next().is_none());
+}
+
 #[test]
 fn product2() {
     let s = "αβ";
diff --git a/tests/test_std.rs b/tests/test_std.rs
index 5d28645..7266fdf 100644
--- a/tests/test_std.rs
+++ b/tests/test_std.rs
@@ -275,7 +275,7 @@ fn all_equal() {
     assert!("A".chars().all_equal());
     assert!(!"AABBCCC".chars().all_equal());
     assert!("AAAAAAA".chars().all_equal());
-    for (_key, mut sub) in &"AABBCCC".chars().group_by(|&x| x) {
+    for (_key, mut sub) in &"AABBCCC".chars().chunk_by(|&x| x) {
         assert!(sub.all_equal());
     }
 }
@@ -797,14 +797,14 @@ fn pad_using() {
 }
 
 #[test]
-fn group_by() {
-    for (ch1, sub) in &"AABBCCC".chars().group_by(|&x| x) {
+fn chunk_by() {
+    for (ch1, sub) in &"AABBCCC".chars().chunk_by(|&x| x) {
         for ch2 in sub {
             assert_eq!(ch1, ch2);
         }
     }
 
-    for (ch1, sub) in &"AAABBBCCCCDDDD".chars().group_by(|&x| x) {
+    for (ch1, sub) in &"AAABBBCCCCDDDD".chars().chunk_by(|&x| x) {
         for ch2 in sub {
             assert_eq!(ch1, ch2);
             if ch1 == 'C' {
@@ -817,8 +817,8 @@ fn group_by() {
 
     // try all possible orderings
     for indices in permutohedron::Heap::new(&mut [0, 1, 2, 3]) {
-        let groups = "AaaBbbccCcDDDD".chars().group_by(&toupper);
-        let mut subs = groups.into_iter().collect_vec();
+        let chunks = "AaaBbbccCcDDDD".chars().chunk_by(&toupper);
+        let mut subs = chunks.into_iter().collect_vec();
 
         for &idx in &indices[..] {
             let (key, text) = match idx {
@@ -833,8 +833,8 @@ fn group_by() {
         }
     }
 
-    let groups = "AAABBBCCCCDDDD".chars().group_by(|&x| x);
-    let mut subs = groups.into_iter().map(|(_, g)| g).collect_vec();
+    let chunks = "AAABBBCCCCDDDD".chars().chunk_by(|&x| x);
+    let mut subs = chunks.into_iter().map(|(_, g)| g).collect_vec();
 
     let sd = subs.pop().unwrap();
     let sc = subs.pop().unwrap();
@@ -851,7 +851,7 @@ fn group_by() {
     {
         let mut ntimes = 0;
         let text = "AABCCC";
-        for (_, sub) in &text.chars().group_by(|&x| {
+        for (_, sub) in &text.chars().chunk_by(|&x| {
             ntimes += 1;
             x
         }) {
@@ -863,7 +863,7 @@ fn group_by() {
     {
         let mut ntimes = 0;
         let text = "AABCCC";
-        for _ in &text.chars().group_by(|&x| {
+        for _ in &text.chars().chunk_by(|&x| {
             ntimes += 1;
             x
         }) {}
@@ -872,81 +872,80 @@ fn group_by() {
 
     {
         let text = "ABCCCDEEFGHIJJKK";
-        let gr = text.chars().group_by(|&x| x);
+        let gr = text.chars().chunk_by(|&x| x);
         it::assert_equal(gr.into_iter().flat_map(|(_, sub)| sub), text.chars());
     }
 }
 
 #[test]
-fn group_by_lazy_2() {
+fn chunk_by_lazy_2() {
     let data = [0, 1];
-    let groups = data.iter().group_by(|k| *k);
-    let gs = groups.into_iter().collect_vec();
+    let chunks = data.iter().chunk_by(|k| *k);
+    let gs = chunks.into_iter().collect_vec();
     it::assert_equal(data.iter(), gs.into_iter().flat_map(|(_k, g)| g));
 
     let data = [0, 1, 1, 0, 0];
-    let groups = data.iter().group_by(|k| *k);
-    let mut gs = groups.into_iter().collect_vec();
+    let chunks = data.iter().chunk_by(|k| *k);
+    let mut gs = chunks.into_iter().collect_vec();
     gs[1..].reverse();
     it::assert_equal(&[0, 0, 0, 1, 1], gs.into_iter().flat_map(|(_, g)| g));
 
-    let grouper = data.iter().group_by(|k| *k);
-    let mut groups = Vec::new();
-    for (k, group) in &grouper {
+    let grouper = data.iter().chunk_by(|k| *k);
+    let mut chunks = Vec::new();
+    for (k, chunk) in &grouper {
         if *k == 1 {
-            groups.push(group);
+            chunks.push(chunk);
         }
     }
-    it::assert_equal(&mut groups[0], &[1, 1]);
+    it::assert_equal(&mut chunks[0], &[1, 1]);
 
     let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
-    let grouper = data.iter().group_by(|k| *k);
-    let mut groups = Vec::new();
-    for (i, (_, group)) in grouper.into_iter().enumerate() {
+    let grouper = data.iter().chunk_by(|k| *k);
+    let mut chunks = Vec::new();
+    for (i, (_, chunk)) in grouper.into_iter().enumerate() {
         if i < 2 {
-            groups.push(group);
+            chunks.push(chunk);
         } else if i < 4 {
-            for _ in group {}
+            for _ in chunk {}
         } else {
-            groups.push(group);
+            chunks.push(chunk);
         }
     }
-    it::assert_equal(&mut groups[0], &[0, 0, 0]);
-    it::assert_equal(&mut groups[1], &[1, 1]);
-    it::assert_equal(&mut groups[2], &[3, 3]);
+    it::assert_equal(&mut chunks[0], &[0, 0, 0]);
+    it::assert_equal(&mut chunks[1], &[1, 1]);
+    it::assert_equal(&mut chunks[2], &[3, 3]);
 
-    // use groups as chunks
     let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
     let mut i = 0;
-    let grouper = data.iter().group_by(move |_| {
+    let grouper = data.iter().chunk_by(move |_| {
         let k = i / 3;
         i += 1;
         k
     });
-    for (i, group) in &grouper {
+    for (i, chunk) in &grouper {
         match i {
-            0 => it::assert_equal(group, &[0, 0, 0]),
-            1 => it::assert_equal(group, &[1, 1, 0]),
-            2 => it::assert_equal(group, &[0, 2, 2]),
-            3 => it::assert_equal(group, &[3, 3]),
+            0 => it::assert_equal(chunk, &[0, 0, 0]),
+            1 => it::assert_equal(chunk, &[1, 1, 0]),
+            2 => it::assert_equal(chunk, &[0, 2, 2]),
+            3 => it::assert_equal(chunk, &[3, 3]),
             _ => unreachable!(),
         }
     }
 }
 
 #[test]
-fn group_by_lazy_3() {
-    // test consuming each group on the lap after it was produced
+fn chunk_by_lazy_3() {
+    // test consuming each chunk on the lap after it was produced
     let data = [0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2];
-    let grouper = data.iter().group_by(|elt| *elt);
+    let grouper = data.iter().chunk_by(|elt| *elt);
     let mut last = None;
-    for (key, group) in &grouper {
+    for (key, chunk) in &grouper {
         if let Some(gr) = last.take() {
             for elt in gr {
                 assert!(elt != key && i32::abs(elt - key) == 1);
             }
         }
-        last = Some(group);
+        last = Some(chunk);
     }
 }