Skip to content

Commit 23bff7a

Browse files
authored
Merge pull request #6 from viruscamp/gen_iter_return
add struct `GenIterReturn` and macro `gen_iter_return!` to iterate over a generator and get the return value
2 parents 0f7d8ef + be06d8d commit 23bff7a

File tree

3 files changed

+336
-142
lines changed

3 files changed

+336
-142
lines changed

src/gen_iter.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use core::ops::{Generator, GeneratorState};
2+
use core::iter::Iterator;
3+
use core::marker::Unpin;
4+
use core::pin::Pin;
5+
6+
/// an iterator that holds an internal generator representing
7+
/// the iteration state
8+
#[derive(Copy, Clone, Debug)]
9+
pub struct GenIter<T>(pub T)
10+
where
11+
T: Generator<Return = ()> + Unpin;
12+
13+
impl<T> Iterator for GenIter<T>
14+
where
15+
T: Generator<Return = ()> + Unpin,
16+
{
17+
type Item = T::Yield;
18+
19+
#[inline]
20+
fn next(&mut self) -> Option<Self::Item> {
21+
match Pin::new(&mut self.0).resume(()) {
22+
GeneratorState::Yielded(n) => Some(n),
23+
GeneratorState::Complete(()) => None,
24+
}
25+
}
26+
}
27+
28+
impl<G> From<G> for GenIter<G>
29+
where
30+
G: Generator<Return = ()> + Unpin,
31+
{
32+
#[inline]
33+
fn from(gen: G) -> Self {
34+
GenIter(gen)
35+
}
36+
}
37+
38+
39+
/// macro to simplify iterator - via - generator construction
40+
///
41+
/// ```
42+
/// #![feature(generators)]
43+
///
44+
/// use gen_iter::gen_iter;
45+
///
46+
/// let mut g = gen_iter!({
47+
/// yield 1;
48+
/// yield 2;
49+
/// });
50+
///
51+
/// assert_eq!(g.next(), Some(1));
52+
/// assert_eq!(g.next(), Some(2));
53+
/// assert_eq!(g.next(), None);
54+
///
55+
/// ```
56+
#[macro_export]
57+
macro_rules! gen_iter {
58+
($block: block) => {
59+
$crate::GenIter(|| $block)
60+
};
61+
(move $block: block) => {
62+
$crate::GenIter(move || $block)
63+
}
64+
}
65+
66+
67+
#[cfg(test)]
68+
mod tests {
69+
use super::GenIter;
70+
71+
#[test]
72+
fn it_works() {
73+
let mut g = gen_iter!({
74+
yield 1;
75+
yield 2;
76+
});
77+
78+
assert_eq!(g.next(), Some(1));
79+
assert_eq!(g.next(), Some(2));
80+
assert_eq!(g.next(), None);
81+
}
82+
83+
#[test]
84+
fn into_gen_iter() {
85+
let mut g: GenIter<_> = (|| {
86+
yield 1;
87+
yield 2;
88+
}).into();
89+
90+
assert_eq!(g.next(), Some(1));
91+
assert_eq!(g.next(), Some(2));
92+
assert_eq!(g.next(), None);
93+
}
94+
95+
#[test]
96+
fn gen_iter_macro() {
97+
let mut g = gen_iter!(move {
98+
yield 1;
99+
yield 2;
100+
});
101+
102+
assert_eq!(g.next(), Some(1));
103+
assert_eq!(g.next(), Some(2));
104+
assert_eq!(g.next(), None);
105+
}
106+
}

src/gen_iter_return.rs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use core::ops::{Generator, GeneratorState};
2+
use core::iter::{Iterator, FusedIterator};
3+
use core::marker::Unpin;
4+
use core::pin::Pin;
5+
6+
/// `GenIterReturn<G>` holds a generator `G` or the return value of `G`,
7+
/// `&mut GenIterReturn<G>` acts as an iterator.
8+
///
9+
/// Differences with `GenIter<G>`:
10+
/// 1. able to get return value of a generator
11+
/// 2. safe to call `next()` after generator is done without panic
12+
/// 3. maybe less efficient than `GenIter<G>`
13+
#[derive(Copy, Clone, Debug)]
14+
pub struct GenIterReturn<G: Generator + Unpin>(Result<G::Return, G>);
15+
16+
impl<G: Generator + Unpin> GenIterReturn<G> {
17+
#[inline]
18+
pub fn new(g: G) -> Self {
19+
GenIterReturn(Err(g))
20+
}
21+
22+
#[inline]
23+
pub fn is_done(&self) -> bool {
24+
self.0.is_ok()
25+
}
26+
27+
#[inline]
28+
pub fn return_or_self(self) -> Result<G::Return, Self> {
29+
match self.0 {
30+
Ok(r) => Ok(r),
31+
Err(_) => Err(self),
32+
}
33+
}
34+
}
35+
36+
/// Force use `&mut g` as iterator to prevent the code below,
37+
/// in which return value cannot be got.
38+
/// ```compile_fail
39+
/// // !!INVALID CODE!!
40+
/// # #![feature(generators)]
41+
/// # use gen_iter::gen_iter_return;
42+
/// let mut g = gen_iter_return!({ yield 1; return "done"; });
43+
/// for v in g {} // invalid, because `GenIterReturn<G>` is not `Iterator`
44+
/// let ret = g.return_or_self(); // g is dropped after for loop
45+
/// ```
46+
impl<G: Generator + Unpin> Iterator for &mut GenIterReturn<G> {
47+
type Item = G::Yield;
48+
49+
#[inline]
50+
fn next(&mut self) -> Option<Self::Item> {
51+
match self.0 {
52+
Ok(_) => None,
53+
Err(ref mut g) => match Pin::new(g).resume(()) {
54+
GeneratorState::Yielded(y) => Some(y),
55+
GeneratorState::Complete(r) => {
56+
self.0 = Ok(r);
57+
None
58+
},
59+
}
60+
}
61+
}
62+
}
63+
64+
/// `GenIterReturn<G>` satisfies the trait `FusedIterator`
65+
impl<G: Generator + Unpin> FusedIterator for &mut GenIterReturn<G> {}
66+
67+
impl<G: Generator + Unpin> From<G> for GenIterReturn<G> {
68+
#[inline]
69+
fn from(g: G) -> Self {
70+
GenIterReturn::new(g)
71+
}
72+
}
73+
74+
/// macro to simplify iterator - via - generator with return value construction
75+
/// ```
76+
/// #![feature(generators)]
77+
///
78+
/// use gen_iter::gen_iter_return;
79+
///
80+
/// let mut g = gen_iter_return!({
81+
/// yield 1;
82+
/// yield 2;
83+
/// return "done";
84+
/// });
85+
///
86+
/// assert_eq!((&mut g).collect::<Vec<_>>(), [1, 2]); // use `&mut g` as an iterator
87+
/// assert_eq!(g.is_done(), true); // check whether generator is done
88+
/// assert_eq!((&mut g).next(), None); // safe to call `next()` after done
89+
/// assert_eq!(g.return_or_self().ok(), Some("done")); // get return value of generator
90+
/// ```
91+
#[macro_export]
92+
macro_rules! gen_iter_return {
93+
($block: block) => {
94+
$crate::GenIterReturn::new(|| $block)
95+
};
96+
(move $block: block) => {
97+
$crate::GenIterReturn::new(move || $block)
98+
}
99+
}
100+
101+
#[cfg(test)]
102+
mod tests {
103+
use super::GenIterReturn;
104+
105+
/// test `new` and all instance method,
106+
/// and show that it won't panic when call `next()` even exhausted.
107+
#[test]
108+
fn it_works() {
109+
let mut g = GenIterReturn::new(|| {
110+
yield 1;
111+
return "done";
112+
});
113+
114+
assert_eq!((&mut g).next(), Some(1));
115+
assert_eq!(g.is_done(), false);
116+
117+
g = match g.return_or_self() {
118+
Ok(_) => panic!("generator is done but should not"),
119+
Err(g) => g
120+
};
121+
122+
assert_eq!((&mut g).next(), None);
123+
assert_eq!(g.is_done(), true);
124+
125+
assert_eq!((&mut g).next(), None); // it won't panic when call `next()` even exhausted.
126+
127+
assert_eq!(g.return_or_self().ok(), Some("done"));
128+
}
129+
130+
#[test]
131+
fn from_generator() {
132+
let mut g = GenIterReturn::from(|| {
133+
yield 1;
134+
return "done";
135+
});
136+
137+
assert_eq!((&mut g).next(), Some(1));
138+
assert_eq!((&mut g).next(), None);
139+
140+
assert_eq!(g.is_done(), true);
141+
assert_eq!(g.return_or_self().ok(), Some("done"));
142+
}
143+
144+
/// normal usage using macro `gen_iter_return`
145+
#[test]
146+
fn macro_usage() {
147+
let mut g = gen_iter_return!(move {
148+
yield 1;
149+
yield 2;
150+
return "done";
151+
});
152+
153+
let (mut sum, mut count) = (0, 0);
154+
for y in &mut g {
155+
sum += y;
156+
count += 1;
157+
}
158+
assert_eq!((sum, count), (3, 2));
159+
160+
assert_eq!(g.is_done(), true);
161+
assert_eq!(g.return_or_self().ok(), Some("done"));
162+
}
163+
}

0 commit comments

Comments
 (0)