Skip to content

Commit 2826141

Browse files
committed
Add note about side effects to Filter docs
Issue rust-lang#30632 shows the unexpected behavior that can arise from `iter::Map` and `iter::Filter` both implementing `DoubleEndedIterator`. PR rust-lang#31220 makes note of this behavior by adding a note to the docs for `iter::Map`, but does not do the same for `iter::Filter`. This PR adds similar documentation to `iter::Filter`. It may be worth considering adding this documentation to `iter::FilterMap` as well or placing it in another location with a note that it applies to using all of these types with stateful closures.
1 parent 06e88c3 commit 2826141

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

library/core/src/iter/adapters/filter.rs

+43
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,49 @@ use core::ops::ControlFlow;
1313
///
1414
/// [`filter`]: Iterator::filter
1515
/// [`Iterator`]: trait.Iterator.html
16+
///
17+
/// # Notes about side effects
18+
///
19+
/// The [`filter`] iterator implements [`DoubleEndedIterator`], meaning that you
20+
/// can also [`filter`] backwards:
21+
///
22+
/// ```rust
23+
/// let v: Vec<i32> = [1, 2, 3].into_iter().filter(|x| *x > 1).rev().collect();
24+
///
25+
/// assert_eq!(v, [3, 2]);
26+
/// ```
27+
///
28+
/// [`DoubleEndedIterator`]: trait.DoubleEndedIterator.html
29+
///
30+
/// But if your closure has state, iterating backwards may act in a way you do
31+
/// not expect. Let's go through an example. First, in the forward direction:
32+
///
33+
/// ```rust
34+
/// let mut c = 0;
35+
///
36+
/// for letter in ['a', 'b', 'c'].into_iter()
37+
/// .filter(|_| { c += 1; c > 1 }) {
38+
/// println!("{letter:?}");
39+
/// }
40+
/// ```
41+
///
42+
/// This will print `'b', 'c'`.
43+
///
44+
/// Now consider this twist where we add a call to `rev`. This version will
45+
/// print `'b', 'a'`. Note that the letters are reversed, but the values of the
46+
/// counter still go in order. This is because `filter()` is still being called
47+
/// lazily on each item, but we are taking items from the back of the array now,
48+
/// instead of shifting them from the front.
49+
///
50+
/// ```rust
51+
/// let mut c = 0;
52+
///
53+
/// for letter in ['a', 'b', 'c'].into_iter()
54+
/// .filter(|_| { c += 1; c > 1 })
55+
/// .rev() {
56+
/// println!("{letter:?}");
57+
/// }
58+
/// ```
1659
#[must_use = "iterators are lazy and do nothing unless consumed"]
1760
#[stable(feature = "rust1", since = "1.0.0")]
1861
#[derive(Clone)]

0 commit comments

Comments
 (0)