Skip to content

Commit de0f9d6

Browse files
author
Andrey Chursin
committed
Make BoxFuture and LocalBoxFuture a concrete type with #[must_use]
Currently BoxFuture is type alias for Pin<Box<...>>, that leads to accidental mistakes, when user does not call await/poll on result of function that returns BoxFuture. We had multiple problems with that Some notes on PR: * I understand that for some projects this might be breaking change - if someone have hardcoded Pin<Box<...>> as a type of variable returned by .boxed, they would have to change it BoxedFuture once this diff lands, but I believe this is reasonable trade off. * I am not sure what to do with Debug implementation for BoxFuture - ok to just suppress warning for this type? * I don't fully understand what UnsafeFutureObj is, I mostly copied implementation from Pin<Box<...>>, but please take a look at it carefully to make sure I did not miss something.
1 parent 6f16a1b commit de0f9d6

File tree

2 files changed

+67
-4
lines changed

2 files changed

+67
-4
lines changed

futures-core/src/future/mod.rs

+65-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ mod future_obj;
1010
pub use self::future_obj::{FutureObj, LocalFutureObj, UnsafeFutureObj};
1111

1212
#[cfg(feature = "alloc")]
13+
#[must_use = "futures do nothing unless polled"]
14+
#[allow(missing_debug_implementations)]
1315
/// An owned dynamically typed [`Future`] for use in cases where you can't
1416
/// statically type your result or need to add some indirection.
15-
pub type BoxFuture<'a, T> = Pin<alloc::boxed::Box<dyn Future<Output = T> + Send + 'a>>;
17+
pub struct BoxFuture<'a, T>(Pin<alloc::boxed::Box<dyn Future<Output = T> + Send + 'a>>);
1618

1719
#[cfg(feature = "alloc")]
20+
#[must_use = "futures do nothing unless polled"]
21+
#[allow(missing_debug_implementations)]
1822
/// `BoxFuture`, but without the `Send` requirement.
19-
pub type LocalBoxFuture<'a, T> = Pin<alloc::boxed::Box<dyn Future<Output = T> + 'a>>;
23+
pub struct LocalBoxFuture<'a, T>(Pin<alloc::boxed::Box<dyn Future<Output = T> + 'a>>);
2024

2125
/// A `Future` or `TryFuture` which tracks whether or not the underlying future
2226
/// should no longer be polled.
@@ -81,6 +85,7 @@ impl<F, T, E> TryFuture for F
8185

8286
#[cfg(feature = "alloc")]
8387
mod if_alloc {
88+
use std::mem;
8489
use alloc::boxed::Box;
8590
use super::*;
8691

@@ -96,4 +101,62 @@ mod if_alloc {
96101
<F as FusedFuture>::is_terminated(&**self)
97102
}
98103
}
104+
105+
impl<O> Future for BoxFuture<'_, O> {
106+
type Output = O;
107+
108+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
109+
let mut_pin = self.0.as_mut();
110+
mut_pin.poll(cx)
111+
}
112+
}
113+
114+
impl<O> BoxFuture<'_, O> {
115+
/// Converts Pin<Box<Future<Output=O>> to PinnedFuture<O>
116+
pub fn new<'a>(f: Pin<Box<dyn Future<Output = O> + Send + 'a>>) -> BoxFuture<'a, O> {
117+
BoxFuture(f)
118+
}
119+
}
120+
121+
impl<O> Future for LocalBoxFuture<'_, O> {
122+
type Output = O;
123+
124+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
125+
let mut_pin = self.0.as_mut();
126+
mut_pin.poll(cx)
127+
}
128+
}
129+
130+
impl<O> LocalBoxFuture<'_, O> {
131+
/// Converts Pin<Box<Future<Output=O>> to PinnedFuture<O>
132+
pub fn new<'a>(f: Pin<Box<dyn Future<Output = O> + 'a>>) -> LocalBoxFuture<'a, O> {
133+
LocalBoxFuture(f)
134+
}
135+
}
136+
137+
unsafe impl<'a, T> UnsafeFutureObj<'a, T> for BoxFuture<'a, T> where T: 'a
138+
{
139+
fn into_raw(mut self) -> *mut (dyn Future<Output = T> + 'a) {
140+
let ptr = unsafe { self.0.as_mut().get_unchecked_mut() as *mut _ };
141+
mem::forget(self);
142+
ptr
143+
}
144+
145+
unsafe fn drop(ptr: *mut (dyn Future<Output = T> + 'a)) {
146+
drop(Pin::from(Box::from_raw(ptr)))
147+
}
148+
}
149+
150+
unsafe impl<'a, T> UnsafeFutureObj<'a, T> for LocalBoxFuture<'a, T> where T: 'a
151+
{
152+
fn into_raw(mut self) -> *mut (dyn Future<Output = T> + 'a) {
153+
let ptr = unsafe { self.0.as_mut().get_unchecked_mut() as *mut _ };
154+
mem::forget(self);
155+
ptr
156+
}
157+
158+
unsafe fn drop(ptr: *mut (dyn Future<Output = T> + 'a)) {
159+
drop(Pin::from(Box::from_raw(ptr)))
160+
}
161+
}
99162
}

futures-util/src/future/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ pub trait FutureExt: Future {
495495
fn boxed<'a>(self) -> BoxFuture<'a, Self::Output>
496496
where Self: Sized + Send + 'a
497497
{
498-
Box::pin(self)
498+
BoxFuture::new(Box::pin(self))
499499
}
500500

501501
/// Wrap the future in a Box, pinning it.
@@ -505,7 +505,7 @@ pub trait FutureExt: Future {
505505
fn boxed_local<'a>(self) -> LocalBoxFuture<'a, Self::Output>
506506
where Self: Sized + 'a
507507
{
508-
Box::pin(self)
508+
LocalBoxFuture::new(Box::pin(self))
509509
}
510510

511511
/// Turns a [`Future<Output = T>`](Future) into a

0 commit comments

Comments
 (0)