Skip to content

Commit 9ecc6b8

Browse files
committed
Update Future combinators to preserve Clone
This updates most `Future` combinators to preserve `Clone` when available on the input `Future`. For motivation, imagine you have some complicated `Future` that is not `Clone` and requires `.shared()` to properly share it. Then imagine you have a library function that is meant to bundle together a bunch of combinators to fulfill some semantic purpose. That library funciton will have to call `.shared()` if it wants to try to guarantee the return `Future` is `Clone`, but this might be suboptimal if the input `Future` was already `Clone`, plus it has the ability to obfuscate and hide the `.shared()` allocation. With this change, you can instead require `Future + Clone` on the input `Future` and have a guarantee the output will be `Clone` as well. The hold-out `Future` implementations are: - `Remote` / `RemoteHandle` due to their use of `futures_channel::oneshot::{Sender, Receiver}`. This seems like it is by design that these cannot be `Clone`. - `JoinAll` / `TryJoinAll` due to their use of `Stream` combinators, but also that it seems unlikely that people would expect them to offer `Clone` since they are used to performa a potentially costly sync barrier that would probably be desired to happen only once. For the hold-outs, the existing pattern of using `.shared()` allows for `Clone`, and follows the intended semantics of those combinators. Some combinators that might not make the most sense to preserve `Clone`: - `IntoStream` - `TryFlattenStream` If these changes make sense, I think it would also make sense to apply them to `Stream` combinators as well (although I don't see myself utilizing this property as much with them). If that is the case, these `Future` -> `Stream` combinators make sense to preserve `Clone`. Tested: - `cargo doc`. - `cargo fmt`. - `cargo test --all-features`.
1 parent c72e229 commit 9ecc6b8

20 files changed

+69
-37
lines changed

futures-util/src/future/future/catch_unwind.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use pin_project_lite::pin_project;
88

99
pin_project! {
1010
/// Future for the [`catch_unwind`](super::FutureExt::catch_unwind) method.
11-
#[derive(Debug)]
11+
#[derive(Clone, Debug)]
1212
#[must_use = "futures do nothing unless you `.await` or poll them"]
1313
pub struct CatchUnwind<Fut> {
1414
#[pin]

futures-util/src/future/future/flatten.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use pin_project_lite::pin_project;
99

1010
pin_project! {
1111
#[project = FlattenProj]
12-
#[derive(Debug)]
12+
#[derive(Clone, Debug)]
1313
pub enum Flatten<Fut1, Fut2> {
1414
First { #[pin] f: Fut1 },
1515
Second { #[pin] f: Fut2 },

futures-util/src/future/future/fuse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use pin_project_lite::pin_project;
55

66
pin_project! {
77
/// Future for the [`fuse`](super::FutureExt::fuse) method.
8-
#[derive(Debug)]
8+
#[derive(Clone, Debug)]
99
#[must_use = "futures do nothing unless you `.await` or poll them"]
1010
pub struct Fuse<Fut> {
1111
#[pin]

futures-util/src/future/future/map.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pin_project! {
1010
/// Internal Map future
1111
#[project = MapProj]
1212
#[project_replace = MapProjReplace]
13-
#[derive(Debug)]
13+
#[derive(Clone, Debug)]
1414
#[must_use = "futures do nothing unless you `.await` or poll them"]
1515
pub enum Map<Fut, F> {
1616
Incomplete {

futures-util/src/future/future/mod.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ delegate_all!(
3030
/// Future for the [`flatten`](super::FutureExt::flatten) method.
3131
Flatten<F>(
3232
flatten::Flatten<F, <F as Future>::Output>
33-
): Debug + Future + FusedFuture + New[|x: F| flatten::Flatten::new(x)]
33+
): Clone + Debug + Future + FusedFuture + New[|x: F| flatten::Flatten::new(x)]
3434
where F: Future
3535
);
3636

3737
delegate_all!(
3838
/// Stream for the [`flatten_stream`](FutureExt::flatten_stream) method.
3939
FlattenStream<F>(
4040
flatten::Flatten<F, <F as Future>::Output>
41-
): Debug + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)]
41+
): Clone + Debug + Sink + Stream + FusedStream + New[|x: F| flatten::Flatten::new(x)]
4242
where F: Future
4343
);
4444

@@ -49,49 +49,49 @@ delegate_all!(
4949
/// Future for the [`map`](super::FutureExt::map) method.
5050
Map<Fut, F>(
5151
map::Map<Fut, F>
52-
): Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, f)]
52+
): Clone + Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, f)]
5353
);
5454

5555
delegate_all!(
5656
/// Stream for the [`into_stream`](FutureExt::into_stream) method.
5757
IntoStream<F>(
5858
crate::stream::Once<F>
59-
): Debug + Stream + FusedStream + New[|x: F| crate::stream::Once::new(x)]
59+
): Clone + Debug + Stream + FusedStream + New[|x: F| crate::stream::Once::new(x)]
6060
);
6161

6262
delegate_all!(
6363
/// Future for the [`map_into`](FutureExt::map_into) combinator.
6464
MapInto<Fut, T>(
6565
Map<Fut, IntoFn<T>>
66-
): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, into_fn())]
66+
): Clone + Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, into_fn())]
6767
);
6868

6969
delegate_all!(
7070
/// Future for the [`then`](FutureExt::then) method.
7171
Then<Fut1, Fut2, F>(
7272
flatten::Flatten<Map<Fut1, F>, Fut2>
73-
): Debug + Future + FusedFuture + New[|x: Fut1, y: F| flatten::Flatten::new(Map::new(x, y))]
73+
): Clone + Debug + Future + FusedFuture + New[|x: Fut1, y: F| flatten::Flatten::new(Map::new(x, y))]
7474
);
7575

7676
delegate_all!(
7777
/// Future for the [`inspect`](FutureExt::inspect) method.
7878
Inspect<Fut, F>(
7979
map::Map<Fut, InspectFn<F>>
80-
): Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, inspect_fn(f))]
80+
): Clone + Debug + Future + FusedFuture + New[|x: Fut, f: F| map::Map::new(x, inspect_fn(f))]
8181
);
8282

8383
delegate_all!(
8484
/// Future for the [`never_error`](super::FutureExt::never_error) combinator.
8585
NeverError<Fut>(
8686
Map<Fut, OkFn<Infallible>>
87-
): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())]
87+
): Clone + Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())]
8888
);
8989

9090
delegate_all!(
9191
/// Future for the [`unit_error`](super::FutureExt::unit_error) combinator.
9292
UnitError<Fut>(
9393
Map<Fut, OkFn<()>>
94-
): Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())]
94+
): Clone + Debug + Future + FusedFuture + New[|x: Fut| Map::new(x, ok_fn())]
9595
);
9696

9797
#[cfg(feature = "std")]

futures-util/src/future/join.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ impl<Fut1: Future, Fut2: Future> Join<Fut1, Fut2> {
2020
}
2121
}
2222

23+
impl<Fut1, Fut2> Clone for Join<Fut1, Fut2>
24+
where
25+
Fut1: Future + Clone,
26+
Fut1::Output: Clone,
27+
Fut2: Future + Clone,
28+
Fut2::Output: Clone,
29+
{
30+
fn clone(&self) -> Self {
31+
Self { fut1: self.fut1.clone(), fut2: self.fut2.clone() }
32+
}
33+
}
34+
2335
impl<Fut1, Fut2> fmt::Debug for Join<Fut1, Fut2>
2436
where
2537
Fut1: Future + fmt::Debug,

futures-util/src/future/lazy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use futures_core::future::{FusedFuture, Future};
44
use futures_core::task::{Context, Poll};
55

66
/// Future for the [`lazy`] function.
7-
#[derive(Debug)]
7+
#[derive(Clone, Debug)]
88
#[must_use = "futures do nothing unless you `.await` or poll them"]
99
pub struct Lazy<F> {
1010
f: Option<F>,

futures-util/src/future/maybe_done.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use futures_core::task::{Context, Poll};
1010
/// A future that may have completed.
1111
///
1212
/// This is created by the [`maybe_done()`] function.
13-
#[derive(Debug)]
13+
#[derive(Clone, Debug)]
1414
pub enum MaybeDone<Fut: Future> {
1515
/// A not-yet-completed future
1616
Future(/* #[pin] */ Fut),

futures-util/src/future/poll_fn.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use futures_core::task::{Context, Poll};
88

99
/// Future for the [`poll_fn`] function.
1010
#[must_use = "futures do nothing unless you `.await` or poll them"]
11+
#[derive(Clone)]
1112
pub struct PollFn<F> {
1213
f: F,
1314
}

futures-util/src/future/select.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use futures_core::task::{Context, Poll};
66

77
/// Future for the [`select()`] function.
88
#[must_use = "futures do nothing unless you `.await` or poll them"]
9-
#[derive(Debug)]
9+
#[derive(Clone, Debug)]
1010
pub struct Select<A, B> {
1111
inner: Option<(A, B)>,
1212
}

0 commit comments

Comments
 (0)