Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9f35f08

Browse files
committedMay 8, 2023
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 5ac72c7 commit 9f35f08

20 files changed

+69
-37
lines changed
 

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

+1-1
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

+1-1
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

+1-1
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

+1-1
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

+9-9
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

+12
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

+1-1
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

+1-1
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

+1
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

+1-1
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
}

‎futures-util/src/future/select_all.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use futures_core::future::Future;
88
use futures_core::task::{Context, Poll};
99

1010
/// Future for the [`select_all`] function.
11-
#[derive(Debug)]
11+
#[derive(Clone, Debug)]
1212
#[must_use = "futures do nothing unless you `.await` or poll them"]
1313
pub struct SelectAll<Fut> {
1414
inner: Vec<Fut>,

‎futures-util/src/future/select_ok.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use futures_core::future::{Future, TryFuture};
88
use futures_core::task::{Context, Poll};
99

1010
/// Future for the [`select_ok`] function.
11-
#[derive(Debug)]
11+
#[derive(Clone, Debug)]
1212
#[must_use = "futures do nothing unless you `.await` or poll them"]
1313
pub struct SelectOk<Fut> {
1414
inner: Vec<Fut>,

‎futures-util/src/future/try_future/into_future.rs

+1-1
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 [`into_future`](super::TryFutureExt::into_future) method.
8-
#[derive(Debug)]
8+
#[derive(Clone, Debug)]
99
#[must_use = "futures do nothing unless you `.await` or poll them"]
1010
pub struct IntoFuture<Fut> {
1111
#[pin]

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

+14-14
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,21 @@ delegate_all!(
3131
/// Future for the [`try_flatten`](TryFutureExt::try_flatten) method.
3232
TryFlatten<Fut1, Fut2>(
3333
try_flatten::TryFlatten<Fut1, Fut2>
34-
): Debug + Future + FusedFuture + New[|x: Fut1| try_flatten::TryFlatten::new(x)]
34+
): Clone + Debug + Future + FusedFuture + New[|x: Fut1| try_flatten::TryFlatten::new(x)]
3535
);
3636

3737
delegate_all!(
3838
/// Future for the [`try_flatten_err`](TryFutureExt::try_flatten_err) method.
3939
TryFlattenErr<Fut1, Fut2>(
4040
try_flatten_err::TryFlattenErr<Fut1, Fut2>
41-
): Debug + Future + FusedFuture + New[|x: Fut1| try_flatten_err::TryFlattenErr::new(x)]
41+
): Clone + Debug + Future + FusedFuture + New[|x: Fut1| try_flatten_err::TryFlattenErr::new(x)]
4242
);
4343

4444
delegate_all!(
4545
/// Future for the [`try_flatten_stream`](TryFutureExt::try_flatten_stream) method.
4646
TryFlattenStream<Fut>(
4747
try_flatten::TryFlatten<Fut, Fut::Ok>
48-
): Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)]
48+
): Clone + Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)]
4949
where Fut: TryFuture
5050
);
5151

@@ -55,49 +55,49 @@ delegate_all!(
5555
#[cfg_attr(docsrs, doc(cfg(feature = "sink")))]
5656
FlattenSink<Fut, Si>(
5757
try_flatten::TryFlatten<Fut, Si>
58-
): Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)]
58+
): Clone + Debug + Sink + Stream + FusedStream + New[|x: Fut| try_flatten::TryFlatten::new(x)]
5959
);
6060

6161
delegate_all!(
6262
/// Future for the [`and_then`](TryFutureExt::and_then) method.
6363
AndThen<Fut1, Fut2, F>(
6464
TryFlatten<MapOk<Fut1, F>, Fut2>
65-
): Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlatten::new(MapOk::new(x, f))]
65+
): Clone + Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlatten::new(MapOk::new(x, f))]
6666
);
6767

6868
delegate_all!(
6969
/// Future for the [`or_else`](TryFutureExt::or_else) method.
7070
OrElse<Fut1, Fut2, F>(
7171
TryFlattenErr<MapErr<Fut1, F>, Fut2>
72-
): Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlattenErr::new(MapErr::new(x, f))]
72+
): Clone + Debug + Future + FusedFuture + New[|x: Fut1, f: F| TryFlattenErr::new(MapErr::new(x, f))]
7373
);
7474

7575
delegate_all!(
7676
/// Future for the [`err_into`](TryFutureExt::err_into) method.
7777
ErrInto<Fut, E>(
7878
MapErr<Fut, IntoFn<E>>
79-
): Debug + Future + FusedFuture + New[|x: Fut| MapErr::new(x, into_fn())]
79+
): Clone + Debug + Future + FusedFuture + New[|x: Fut| MapErr::new(x, into_fn())]
8080
);
8181

8282
delegate_all!(
8383
/// Future for the [`ok_into`](TryFutureExt::ok_into) method.
8484
OkInto<Fut, E>(
8585
MapOk<Fut, IntoFn<E>>
86-
): Debug + Future + FusedFuture + New[|x: Fut| MapOk::new(x, into_fn())]
86+
): Clone + Debug + Future + FusedFuture + New[|x: Fut| MapOk::new(x, into_fn())]
8787
);
8888

8989
delegate_all!(
9090
/// Future for the [`inspect_ok`](super::TryFutureExt::inspect_ok) method.
9191
InspectOk<Fut, F>(
9292
Inspect<IntoFuture<Fut>, InspectOkFn<F>>
93-
): Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_ok_fn(f))]
93+
): Clone + Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_ok_fn(f))]
9494
);
9595

9696
delegate_all!(
9797
/// Future for the [`inspect_err`](super::TryFutureExt::inspect_err) method.
9898
InspectErr<Fut, F>(
9999
Inspect<IntoFuture<Fut>, InspectErrFn<F>>
100-
): Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_err_fn(f))]
100+
): Clone + Debug + Future + FusedFuture + New[|x: Fut, f: F| Inspect::new(IntoFuture::new(x), inspect_err_fn(f))]
101101
);
102102

103103
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
@@ -107,28 +107,28 @@ delegate_all!(
107107
/// Future for the [`map_ok`](TryFutureExt::map_ok) method.
108108
MapOk<Fut, F>(
109109
Map<IntoFuture<Fut>, MapOkFn<F>>
110-
): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_ok_fn(f))]
110+
): Clone + Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_ok_fn(f))]
111111
);
112112

113113
delegate_all!(
114114
/// Future for the [`map_err`](TryFutureExt::map_err) method.
115115
MapErr<Fut, F>(
116116
Map<IntoFuture<Fut>, MapErrFn<F>>
117-
): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_err_fn(f))]
117+
): Clone + Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), map_err_fn(f))]
118118
);
119119

120120
delegate_all!(
121121
/// Future for the [`map_ok_or_else`](TryFutureExt::map_ok_or_else) method.
122122
MapOkOrElse<Fut, F, G>(
123123
Map<IntoFuture<Fut>, MapOkOrElseFn<F, G>>
124-
): Debug + Future + FusedFuture + New[|x: Fut, f: F, g: G| Map::new(IntoFuture::new(x), map_ok_or_else_fn(f, g))]
124+
): Clone + Debug + Future + FusedFuture + New[|x: Fut, f: F, g: G| Map::new(IntoFuture::new(x), map_ok_or_else_fn(f, g))]
125125
);
126126

127127
delegate_all!(
128128
/// Future for the [`unwrap_or_else`](TryFutureExt::unwrap_or_else) method.
129129
UnwrapOrElse<Fut, F>(
130130
Map<IntoFuture<Fut>, UnwrapOrElseFn<F>>
131-
): Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))]
131+
): Clone + Debug + Future + FusedFuture + New[|x: Fut, f: F| Map::new(IntoFuture::new(x), unwrap_or_else_fn(f))]
132132
);
133133

134134
impl<Fut: ?Sized + TryFuture> TryFutureExt for Fut {}

‎futures-util/src/future/try_future/try_flatten.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use pin_project_lite::pin_project;
99

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

‎futures-util/src/future/try_future/try_flatten_err.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use pin_project_lite::pin_project;
66

77
pin_project! {
88
#[project = TryFlattenErrProj]
9-
#[derive(Debug)]
9+
#[derive(Clone, Debug)]
1010
pub enum TryFlattenErr<Fut1, Fut2> {
1111
First { #[pin] f: Fut1 },
1212
Second { #[pin] f: Fut2 },

‎futures-util/src/future/try_join.rs

+12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ impl<Fut1: TryFuture, Fut2: TryFuture> TryJoin<Fut1, Fut2> {
2020
}
2121
}
2222

23+
impl<Fut1, Fut2> Clone for TryJoin<Fut1, Fut2>
24+
where
25+
Fut1: TryFuture + Clone,
26+
Fut1::Ok: Clone,
27+
Fut2: TryFuture + Clone,
28+
Fut2::Ok: 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 TryJoin<Fut1, Fut2>
2436
where
2537
Fut1: TryFuture + fmt::Debug,

‎futures-util/src/future/try_maybe_done.rs

+1-1
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 with an error.
1111
///
1212
/// This is created by the [`try_maybe_done()`] function.
13-
#[derive(Debug)]
13+
#[derive(Clone, Debug)]
1414
pub enum TryMaybeDone<Fut: TryFuture> {
1515
/// A not-yet-completed future
1616
Future(/* #[pin] */ Fut),

‎futures-util/src/future/try_select.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use futures_core::task::{Context, Poll};
55

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

‎futures-util/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,13 @@ macro_rules! delegate_all {
253253
delegate_sink!(inner, _Item);
254254
}
255255
};
256+
(@trait Clone $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
257+
impl<$($arg),*> Clone for $name<$($arg),*> where $t: Clone $(, $($bound)*)* {
258+
fn clone(&self) -> Self {
259+
Self { inner: Clone::clone(&self.inner) }
260+
}
261+
}
262+
};
256263
(@trait Debug $name:ident < $($arg:ident),* > ($t:ty) $(where $($bound:tt)*)*) => {
257264
impl<$($arg),*> core::fmt::Debug for $name<$($arg),*> where $t: core::fmt::Debug $(, $($bound)*)* {
258265
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {

0 commit comments

Comments
 (0)
Please sign in to comment.