Skip to content

Commit b34addc

Browse files
committed
Add future::pinned combinator
1 parent 99ec3c2 commit b34addc

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

futures-util/src/future/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ use futures_core::{Future, Stream};
99
// Primitive futures
1010
mod empty;
1111
mod lazy;
12+
mod pinned;
1213
mod poll_fn;
1314
pub use self::empty::{empty, Empty};
1415
pub use self::lazy::{lazy, Lazy};
16+
pub use self::pinned::{pinned, PinnedFut};
1517
pub use self::poll_fn::{poll_fn, PollFn};
1618

1719
// combinators

futures-util/src/future/pinned.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! Definition of the `PinnedFut` adapter combinator
2+
3+
// TODO: import `Pinned` and use it to make `Borrowed` immovable.
4+
use core::mem::PinMut;
5+
6+
use futures_core::{Future, Poll};
7+
use futures_core::task;
8+
9+
pub trait PinnedFnLt<'a, Data: 'a, Output> {
10+
type Future: Future<Output = Output> + 'a;
11+
fn apply(self, data: PinMut<'a, Data>) -> Self::Future;
12+
}
13+
14+
pub trait PinnedFn<Data, Output>: for<'a> PinnedFnLt<'a, Data, Output> {}
15+
impl<Data, Output, T> PinnedFn<Data, Output> for T
16+
where T: for<'a> PinnedFnLt<'a, Data, Output> {}
17+
18+
impl<'a, Data, Output, Fut, T> PinnedFnLt<'a, Data, Output> for T
19+
where
20+
Data: 'a,
21+
T: FnOnce(PinMut<'a, Data>) -> Fut,
22+
Fut: Future<Output = Output> + 'a,
23+
{
24+
type Future = Fut;
25+
fn apply(self, data: PinMut<'a, Data>) -> Self::Future {
26+
(self)(data)
27+
}
28+
}
29+
30+
/// A future which borrows a value for an asynchronous lifetime.
31+
///
32+
/// Created by the `borrowed` function.
33+
#[must_use = "futures do nothing unless polled"]
34+
#[allow(missing_debug_implementations)]
35+
pub struct PinnedFut<'any, Data: 'any, Output, F: PinnedFn<Data, Output> + 'any> {
36+
fn_or_fut: FnOrFut<'any, Data, Output, F>,
37+
// TODO:
38+
// marker: Pinned,
39+
// Data, which may be borrowed by `fn_or_fut`, must be dropped last
40+
data: Data,
41+
}
42+
43+
enum FnOrFut<'any, Data: 'any, Output, F: PinnedFn<Data, Output> + 'any> {
44+
F(F),
45+
Fut(<F as PinnedFnLt<'any, Data, Output>>::Future),
46+
None,
47+
}
48+
49+
impl<'any, Data: 'any, Output, F: PinnedFn<Data, Output> + 'any> FnOrFut<'any, Data, Output, F> {
50+
fn is_fn(&self) -> bool {
51+
if let FnOrFut::F(_) = self {
52+
true
53+
} else {
54+
false
55+
}
56+
}
57+
}
58+
59+
/// Creates a new future which pins some data and borrows it for an
60+
/// asynchronous lifetime.
61+
pub fn pinned<'any, Data, Output, F>(data: Data, f: F) -> PinnedFut<'any, Data, Output, F>
62+
where F: PinnedFn<Data, Output> + 'any,
63+
Data: 'any,
64+
{
65+
PinnedFut {
66+
fn_or_fut: FnOrFut::F(f),
67+
data,
68+
}
69+
}
70+
71+
unsafe fn transmute_lt<'input, 'output, T>(x: &'input mut T) -> &'output mut T {
72+
::std::mem::transmute(x)
73+
}
74+
75+
impl<'any, Data, Output, F> Future for PinnedFut<'any, Data, Output, F>
76+
where F: PinnedFn<Data, Output> + 'any,
77+
Data: 'any,
78+
{
79+
type Output = <<F as PinnedFnLt<'any, Data, Output>>::Future as Future>::Output;
80+
81+
fn poll(self: PinMut<Self>, cx: &mut task::Context) -> Poll<Self::Output> {
82+
unsafe {
83+
let this = PinMut::get_mut(self);
84+
if this.fn_or_fut.is_fn() {
85+
if let FnOrFut::F(f) = ::std::mem::replace(&mut this.fn_or_fut, FnOrFut::None) {
86+
let fut = f.apply(PinMut::new_unchecked(transmute_lt(&mut this.data)));
87+
this.fn_or_fut = FnOrFut::Fut(fut);
88+
} else {
89+
unreachable!()
90+
}
91+
}
92+
93+
let res = if let FnOrFut::Fut(fut) = &mut this.fn_or_fut {
94+
PinMut::new_unchecked(fut).poll(cx)
95+
} else {
96+
panic!("polled PinnedFut after completion")
97+
};
98+
99+
if let Poll::Ready(_) = &res {
100+
this.fn_or_fut = FnOrFut::None;
101+
}
102+
103+
res
104+
}
105+
}
106+
}
107+
108+
#[allow(unused)]
109+
fn does_compile() -> impl Future<Output = u8> {
110+
pinned(5, |x: PinMut<_>| { // This type annotation is *required* to compile
111+
::future::lazy(move |_cx| {
112+
// we can use (copy from) the asynchronously borrowed data here
113+
*x
114+
})
115+
})
116+
}

0 commit comments

Comments
 (0)