Skip to content

Commit d970945

Browse files
taiki-ecramertj
authored andcommitted
Encapsulate unsafe code
1 parent ca26c7b commit d970945

File tree

2 files changed

+43
-29
lines changed

2 files changed

+43
-29
lines changed

futures-util/src/future/flatten_stream.rs

+21-15
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ use pin_utils::unsafe_pinned;
88
/// Stream for the [`flatten_stream`](super::FutureExt::flatten_stream) method.
99
#[must_use = "streams do nothing unless polled"]
1010
pub struct FlattenStream<Fut: Future> {
11-
state: State<Fut>
11+
state: State<Fut, Fut::Output>,
1212
}
1313

1414
impl<Fut: Future> FlattenStream<Fut> {
15-
unsafe_pinned!(state: State<Fut>);
15+
unsafe_pinned!(state: State<Fut, Fut::Output>);
1616

1717
pub(super) fn new(future: Fut) -> FlattenStream<Fut> {
1818
FlattenStream {
@@ -33,11 +33,25 @@ impl<Fut> fmt::Debug for FlattenStream<Fut>
3333
}
3434

3535
#[derive(Debug)]
36-
enum State<Fut: Future> {
36+
enum State<Fut, St> {
3737
// future is not yet called or called and not ready
3838
Future(Fut),
3939
// future resolved to Stream
40-
Stream(Fut::Output),
40+
Stream(St),
41+
}
42+
43+
impl<Fut, St> State<Fut, St> {
44+
fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> State<Pin<&'a mut Fut>, Pin<&'a mut St>> {
45+
// safety: data is never moved via the resulting &mut reference
46+
match unsafe { Pin::get_unchecked_mut(self) } {
47+
// safety: the future we're re-pinning here will never be moved;
48+
// it will just be polled, then dropped in place
49+
State::Future(f) => State::Future(unsafe { Pin::new_unchecked(f) }),
50+
// safety: the stream we're repinning here will never be moved;
51+
// it will just be polled, then dropped in place
52+
State::Stream(s) => State::Stream(unsafe { Pin::new_unchecked(s) }),
53+
}
54+
}
4155
}
4256

4357
impl<Fut> FusedStream for FlattenStream<Fut>
@@ -60,23 +74,15 @@ impl<Fut> Stream for FlattenStream<Fut>
6074

6175
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
6276
loop {
63-
// safety: data is never moved via the resulting &mut reference
64-
match &mut unsafe { Pin::get_unchecked_mut(self.as_mut()) }.state {
77+
match self.as_mut().state().get_pin_mut() {
6578
State::Future(f) => {
66-
// safety: the future we're re-pinning here will never be moved;
67-
// it will just be polled, then dropped in place
68-
let stream = ready!(unsafe { Pin::new_unchecked(f) }.poll(cx));
69-
79+
let stream = ready!(f.poll(cx));
7080
// Future resolved to stream.
7181
// We do not return, but poll that
7282
// stream in the next loop iteration.
7383
self.as_mut().state().set(State::Stream(stream));
7484
}
75-
State::Stream(s) => {
76-
// safety: the stream we're repinning here will never be moved;
77-
// it will just be polled, then dropped in place
78-
return unsafe { Pin::new_unchecked(s) }.poll_next(cx);
79-
}
85+
State::Stream(s) => return s.poll_next(cx),
8086
}
8187
}
8288
}

futures-util/src/try_future/try_flatten_stream.rs

+22-14
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ pub struct TryFlattenStream<Fut>
1111
where
1212
Fut: TryFuture,
1313
{
14-
state: State<Fut>
14+
state: State<Fut, Fut::Ok>,
1515
}
1616

1717
impl<Fut: TryFuture> TryFlattenStream<Fut>
1818
where
1919
Fut: TryFuture,
2020
Fut::Ok: TryStream<Error = Fut::Error>,
2121
{
22-
unsafe_pinned!(state: State<Fut>);
22+
unsafe_pinned!(state: State<Fut, Fut::Ok>);
2323

2424
pub(super) fn new(future: Fut) -> Self {
2525
Self {
@@ -41,15 +41,30 @@ where
4141
}
4242

4343
#[derive(Debug)]
44-
enum State<Fut: TryFuture> {
44+
enum State<Fut, St> {
4545
// future is not yet called or called and not ready
4646
Future(Fut),
4747
// future resolved to Stream
48-
Stream(Fut::Ok),
48+
Stream(St),
4949
// future resolved to error
5050
Done,
5151
}
5252

53+
impl<Fut, St> State<Fut, St> {
54+
fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> State<Pin<&'a mut Fut>, Pin<&'a mut St>> {
55+
// safety: data is never moved via the resulting &mut reference
56+
match unsafe { Pin::get_unchecked_mut(self) } {
57+
// safety: the future we're re-pinning here will never be moved;
58+
// it will just be polled, then dropped in place
59+
State::Future(f) => State::Future(unsafe { Pin::new_unchecked(f) }),
60+
// safety: the stream we're repinning here will never be moved;
61+
// it will just be polled, then dropped in place
62+
State::Stream(s) => State::Stream(unsafe { Pin::new_unchecked(s) }),
63+
State::Done => State::Done,
64+
}
65+
}
66+
}
67+
5368
impl<Fut> FusedStream for TryFlattenStream<Fut>
5469
where
5570
Fut: TryFuture,
@@ -73,12 +88,9 @@ where
7388

7489
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
7590
loop {
76-
// safety: data is never moved via the resulting &mut reference
77-
match &mut unsafe { Pin::get_unchecked_mut(self.as_mut()) }.state {
91+
match self.as_mut().state().get_pin_mut() {
7892
State::Future(f) => {
79-
// safety: the future we're re-pinning here will never be moved;
80-
// it will just be polled, then dropped in place
81-
match ready!(unsafe { Pin::new_unchecked(f) }.try_poll(cx)) {
93+
match ready!(f.try_poll(cx)) {
8294
Ok(stream) => {
8395
// Future resolved to stream.
8496
// We do not return, but poll that
@@ -93,11 +105,7 @@ where
93105
}
94106
}
95107
}
96-
State::Stream(s) => {
97-
// safety: the stream we're repinning here will never be moved;
98-
// it will just be polled, then dropped in place
99-
return unsafe { Pin::new_unchecked(s) }.try_poll_next(cx);
100-
}
108+
State::Stream(s) => return s.try_poll_next(cx),
101109
State::Done => return Poll::Ready(None),
102110
}
103111
}

0 commit comments

Comments
 (0)