1#![cfg_attr(test, feature(test))]
12
13use std::fmt;
14use std::iter::FusedIterator;
15
16pub trait IteratorOptionalFilterExt<P: FnMut(&Self::Item) -> bool>: Iterator + Sized {
18 fn optional_filter(self, predicate: Option<P>) -> OptionalFilter<Self, P> {
52 OptionalFilter {
53 iter: self,
54 predicate,
55 }
56 }
57}
58
59impl<I, P> IteratorOptionalFilterExt<P> for I
60where
61 I: Iterator,
62 P: FnMut(&I::Item) -> bool
63{}
64#[must_use = "iterators are lazy and do nothing unless consumed"]
67#[derive(Clone)]
68pub struct OptionalFilter<I, P> {
69 iter: I,
70 predicate: Option<P>,
71}
72
73impl<I, P> Iterator for OptionalFilter<I, P>
74where
75 I: Iterator,
76 P: FnMut(&I::Item) -> bool
77{
78 type Item = I::Item;
79
80 #[inline]
81 fn next(&mut self) -> Option<Self::Item> {
82 match &mut self.predicate {
83 Some(predicate) => self.iter.find(predicate),
84 None => self.iter.next(),
85 }
86 }
87
88 #[inline]
89 fn size_hint(&self) -> (usize, Option<usize>) {
90 let (lower, upper) = self.iter.size_hint();
91 if self.predicate.is_some() {
92 (0, upper)
93 }
94 else {
95 (lower, upper)
96 }
97 }
98
99 #[inline]
101 fn count(self) -> usize {
102 #[inline]
103 fn to_usize<T>(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut(T) -> usize {
104 move |x| predicate(&x) as usize
105 }
106
107 match self.predicate {
108 Some(predicate) => self.iter.map(to_usize(predicate)).sum(),
109 None => self.iter.count(),
110 }
111 }
112}
113
114impl<I, P> DoubleEndedIterator for OptionalFilter<I, P>
115where
116 I: DoubleEndedIterator,
117 P: FnMut(&I::Item) -> bool
118{
119 fn next_back(&mut self) -> Option<Self::Item> {
120 match &mut self.predicate {
121 Some(predicate) => self.iter.rfind(predicate),
122 None => self.iter.next_back(),
123 }
124 }
125}
126
127impl<I, P> FusedIterator for OptionalFilter<I, P>
128where
129 I: FusedIterator,
130 P: FnMut(&I::Item) -> bool
131{}
132
133impl<I: fmt::Debug, P> fmt::Debug for OptionalFilter<I, P> {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 f.debug_struct("OptionalFilter")
136 .field("iter", &self.iter)
137 .finish()
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use crate::*;
144 extern crate test;
145
146 #[test]
147 fn const_enabled() {
148 let output: Vec<_> = (0..10).optional_filter(Some(|&item: &usize| item % 2 == 0)).collect();
149 assert_eq!(output, vec![0, 2, 4, 6, 8])
150 }
151
152 #[test]
153 fn const_disabled() {
154 let output: Vec<_> = (0..10).optional_filter(None::<fn(&usize) -> bool>).collect();
155 assert_eq!(output, (0..10).collect::<Vec<_>>())
156 }
157
158 #[test]
159 fn double_ended_enabled() {
160 let mut iter = (0..10).optional_filter(Some(|&item: &usize| item % 2 == 0));
161 assert_eq!(iter.next(), Some(0));
162 assert_eq!(iter.next_back(), Some(8));
163 assert_eq!(iter.next_back(), Some(6));
164 assert_eq!(iter.next_back(), Some(4));
165 assert_eq!(iter.next(), Some(2));
166 assert_eq!(iter.next(), None);
167 assert_eq!(iter.next_back(), None);
168 }
169
170 #[test]
171 fn nested_const() {
172 let output: Vec<_> = (0..10)
173 .optional_filter(Some(|&item: &usize| item % 2 == 0))
174 .optional_filter(None::<fn(&usize) -> bool>)
175 .optional_filter(Some(|&item: &usize| item % 3 == 0))
176 .collect();
177 assert_eq!(output, vec![0, 6])
178 }
179
180 fn bool_to_opt<T>(b: bool, some: T) -> Option<T> {
182 if b {
183 Some(some)
184 }
185 else {
186 None
187 }
188 }
189
190 fn filter_nested(cond1: bool, cond2: bool) -> Vec<usize> {
191 let cond1 = test::black_box(cond1);
192 let cond2 = test::black_box(cond2);
193 (0..10)
194 .optional_filter(bool_to_opt(cond1, |&item: &usize| item % 2 == 0))
195 .optional_filter(bool_to_opt(cond2, |&item: &usize| item % 3 == 0))
196 .collect()
197 }
198
199 #[test]
200 fn nested_false_false() {
201 assert_eq!(filter_nested(false, false), (0..10).collect::<Vec<usize>>());
202 }
203
204 #[test]
205 fn nested_false_true() {
206 assert_eq!(filter_nested(false, true), vec![0, 3, 6, 9]);
207 }
208
209 #[test]
210 fn nested_true_false() {
211 assert_eq!(filter_nested(true, false), vec![0, 2, 4, 6, 8]);
212 }
213
214 #[test]
215 fn nested_true_true() {
216 assert_eq!(filter_nested(true, true), vec![0, 6]);
217 }
218
219 mod benches {
220 use super::*;
221 use test::Bencher;
222
223 trait IteratorOptionalFilterBoxedExt<'a, P: FnMut(&Self::Item) -> bool>
224 where
225 Self: Iterator + Sized + 'a,
226 P: 'a
227 {
228 fn optional_filter_boxed(self, predicate: Option<P>) -> Box<dyn Iterator<Item = Self::Item> + 'a>
229 {
230 match predicate {
231 Some(predicate) => Box::new(self.filter(predicate)),
232 None => Box::new(self),
233 }
234 }
235 }
236
237 impl<'a, I, P> IteratorOptionalFilterBoxedExt<'a, P> for I
238 where
239 I: Iterator + 'a,
240 P: FnMut(&I::Item) -> bool + 'a
241 {}
242
243 mod collect_vec {
244 use super::*;
245 #[bench]
246 fn disabled_baseline(b: &mut Bencher) {
247 b.iter(|| {
248 let n: usize = test::black_box(1_000_000);
249 (0..n).collect::<Vec<_>>()
250 })
251 }
252
253 #[bench]
254 fn disabled_blackbox(b: &mut Bencher) {
255 b.iter(|| {
256 let n: usize = test::black_box(1_000_000);
257 let f: Option<fn(&usize) -> bool> = test::black_box(None);
258 (0..n).optional_filter(f).collect::<Vec<_>>()
259 })
260 }
261
262 #[bench]
263 fn disabled_blackbox_boxed(b: &mut Bencher) {
264 b.iter(|| {
265 let n: usize = test::black_box(1_000_000);
266 let f: Option<fn(&usize) -> bool> = test::black_box(None);
267 (0..n).optional_filter_boxed(f).collect::<Vec<_>>()
268 })
269 }
270
271 #[bench]
272 fn disabled_const(b: &mut Bencher) {
273 b.iter(|| {
274 let n: usize = test::black_box(1_000_000);
275 (0..n).optional_filter(None::<fn(&usize) -> bool>).collect::<Vec<_>>()
276 })
277 }
278
279 #[bench]
280 fn disabled_const_boxed(b: &mut Bencher) {
281 b.iter(|| {
282 let n: usize = test::black_box(1_000_000);
283 (0..n).optional_filter_boxed(None::<fn(&usize) -> bool>).collect::<Vec<_>>()
284 })
285 }
286
287 #[bench]
288 fn enabled_baseline(b: &mut Bencher) {
289 b.iter(|| {
290 let n: usize = test::black_box(1_000_000);
291 (0..n).filter(|&item| item % 2 == 0).collect::<Vec<_>>()
292 })
293 }
294
295 #[bench]
296 fn enabled_blackbox(b: &mut Bencher) {
297 b.iter(|| {
298 let n: usize = test::black_box(1_000_000);
299 let f: Option<fn(&usize) -> bool> = test::black_box(Some(|&item| item % 2 == 0));
300 (0..n).optional_filter(f).collect::<Vec<_>>()
301 })
302 }
303
304 #[bench]
305 fn enabled_blackbox_boxed(b: &mut Bencher) {
306 b.iter(|| {
307 let n: usize = test::black_box(1_000_000);
308 let f: Option<fn(&usize) -> bool> = test::black_box(Some(|&item| item % 2 == 0));
309 (0..n).optional_filter_boxed(f).collect::<Vec<_>>()
310 })
311 }
312
313 #[bench]
314 fn enabled_const(b: &mut Bencher) {
315 b.iter(|| {
316 let n: usize = test::black_box(1_000_000);
317 let f: Option<fn(&usize) -> bool> = Some(|&item| item % 2 == 0);
318 (0..n).optional_filter(f).collect::<Vec<_>>()
319 })
320 }
321
322 #[bench]
323 fn enabled_const_boxed(b: &mut Bencher) {
324 b.iter(|| {
325 let n: usize = test::black_box(1_000_000);
326 let f: Option<fn(&usize) -> bool> = Some(|&item| item % 2 == 0);
327 (0..n).optional_filter_boxed(f).collect::<Vec<_>>()
328 })
329 }
330 }
331
332 mod count {
333 use super::*;
334 #[bench]
335 fn disabled_baseline(b: &mut Bencher) {
336 b.iter(|| {
337 let n: usize = test::black_box(1_000_000);
338 (0..n).count()
339 })
340 }
341
342 #[bench]
343 fn disabled_blackbox(b: &mut Bencher) {
344 b.iter(|| {
345 let n: usize = test::black_box(1_000_000);
346 let f: Option<fn(&usize) -> bool> = test::black_box(None);
347 (0..n).optional_filter(f).count()
348 })
349 }
350
351 #[bench]
352 fn disabled_blackbox_boxed(b: &mut Bencher) {
353 b.iter(|| {
354 let n: usize = test::black_box(1_000_000);
355 let f: Option<fn(&usize) -> bool> = test::black_box(None);
356 (0..n).optional_filter_boxed(f).count()
357 })
358 }
359
360 #[bench]
361 fn disabled_const(b: &mut Bencher) {
362 b.iter(|| {
363 let n: usize = test::black_box(1_000_000);
364 (0..n).optional_filter(None::<fn(&usize) -> bool>).count()
365 })
366 }
367
368 #[bench]
369 fn disabled_const_boxed(b: &mut Bencher) {
370 b.iter(|| {
371 let n: usize = test::black_box(1_000_000);
372 (0..n).optional_filter_boxed(None::<fn(&usize) -> bool>).count()
373 })
374 }
375
376 #[bench]
377 fn enabled_baseline(b: &mut Bencher) {
378 b.iter(|| {
379 let n: usize = test::black_box(1_000_000);
380 (0..n).filter(|&item| item % 2 == 0).count()
381 })
382 }
383
384 #[bench]
385 fn enabled_blackbox(b: &mut Bencher) {
386 b.iter(|| {
387 let n: usize = test::black_box(1_000_000);
388 let f: Option<fn(&usize) -> bool> = test::black_box(Some(|&item| item % 2 == 0));
389 (0..n).optional_filter(f).count()
390 })
391 }
392
393 #[bench]
394 fn enabled_blackbox_boxed(b: &mut Bencher) {
395 b.iter(|| {
396 let n: usize = test::black_box(1_000_000);
397 let f: Option<fn(&usize) -> bool> = test::black_box(Some(|&item| item % 2 == 0));
398 (0..n).optional_filter_boxed(f).count()
399 })
400 }
401
402 #[bench]
403 fn enabled_const(b: &mut Bencher) {
404 b.iter(|| {
405 let n: usize = test::black_box(1_000_000);
406 let f: Option<fn(&usize) -> bool> = Some(|&item| item % 2 == 0);
407 (0..n).optional_filter(f).count()
408 })
409 }
410
411 #[bench]
412 fn enabled_const_boxed(b: &mut Bencher) {
413 b.iter(|| {
414 let n: usize = test::black_box(1_000_000);
415 let f: Option<fn(&usize) -> bool> = Some(|&item| item % 2 == 0);
416 (0..n).optional_filter_boxed(f).count()
417 })
418 }
419 }
420
421 mod sum {
422 use super::*;
423 #[bench]
424 fn disabled_baseline(b: &mut Bencher) {
425 b.iter(|| {
426 let n: usize = test::black_box(1_000_000);
427 (0..n).sum::<usize>()
428 })
429 }
430
431 #[bench]
432 fn disabled_blackbox(b: &mut Bencher) {
433 b.iter(|| {
434 let n: usize = test::black_box(1_000_000);
435 let f: Option<fn(&usize) -> bool> = test::black_box(None);
436 (0..n).optional_filter(f).sum::<usize>()
437 })
438 }
439
440 #[bench]
441 fn disabled_blackbox_boxed(b: &mut Bencher) {
442 b.iter(|| {
443 let n: usize = test::black_box(1_000_000);
444 let f: Option<fn(&usize) -> bool> = test::black_box(None);
445 (0..n).optional_filter_boxed(f).sum::<usize>()
446 })
447 }
448
449 #[bench]
450 fn disabled_const(b: &mut Bencher) {
451 b.iter(|| {
452 let n: usize = test::black_box(1_000_000);
453 (0..n).optional_filter(None::<fn(&usize) -> bool>).sum::<usize>()
454 })
455 }
456
457 #[bench]
458 fn disabled_const_boxed(b: &mut Bencher) {
459 b.iter(|| {
460 let n: usize = test::black_box(1_000_000);
461 (0..n).optional_filter_boxed(None::<fn(&usize) -> bool>).sum::<usize>()
462 })
463 }
464
465 #[bench]
466 fn enabled_baseline(b: &mut Bencher) {
467 b.iter(|| {
468 let n: usize = test::black_box(1_000_000);
469 (0..n).filter(|&item| item % 2 == 0).sum::<usize>()
470 })
471 }
472
473 #[bench]
474 fn enabled_blackbox(b: &mut Bencher) {
475 b.iter(|| {
476 let n: usize = test::black_box(1_000_000);
477 let f: Option<fn(&usize) -> bool> = test::black_box(Some(|&item| item % 2 == 0));
478 (0..n).optional_filter(f).sum::<usize>()
479 })
480 }
481
482 #[bench]
483 fn enabled_blackbox_boxed(b: &mut Bencher) {
484 b.iter(|| {
485 let n: usize = test::black_box(1_000_000);
486 let f: Option<fn(&usize) -> bool> = test::black_box(Some(|&item| item % 2 == 0));
487 (0..n).optional_filter_boxed(f).sum::<usize>()
488 })
489 }
490
491 #[bench]
492 fn enabled_const(b: &mut Bencher) {
493 b.iter(|| {
494 let n: usize = test::black_box(1_000_000);
495 let f: Option<fn(&usize) -> bool> = Some(|&item| item % 2 == 0);
496 (0..n).optional_filter(f).sum::<usize>()
497 })
498 }
499
500 #[bench]
501 fn enabled_const_boxed(b: &mut Bencher) {
502 b.iter(|| {
503 let n: usize = test::black_box(1_000_000);
504 let f: Option<fn(&usize) -> bool> = Some(|&item| item % 2 == 0);
505 (0..n).optional_filter_boxed(f).sum::<usize>()
506 })
507 }
508 }
509 }
510}