From 4e0d99bfda36ec4a88accf56bfa1e333b126758c Mon Sep 17 00:00:00 2001 From: Andrey Chursin Date: Fri, 28 Jun 2019 11:18:34 -0700 Subject: [PATCH] Make BoxFuture and LocalBoxFuture a concrete type with #[must_use] Currently BoxFuture is type alias for Pin>, 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> 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>, but please take a look at it carefully to make sure I did not miss something. --- futures-core/src/future/mod.rs | 67 +++++++++++++++++++++++++++++++++- futures-util/src/future/mod.rs | 4 +- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/futures-core/src/future/mod.rs b/futures-core/src/future/mod.rs index 110a8289fe..ae654aa17c 100644 --- a/futures-core/src/future/mod.rs +++ b/futures-core/src/future/mod.rs @@ -10,13 +10,17 @@ mod future_obj; pub use self::future_obj::{FutureObj, LocalFutureObj, UnsafeFutureObj}; #[cfg(feature = "alloc")] +#[must_use = "futures do nothing unless polled"] +#[allow(missing_debug_implementations)] /// An owned dynamically typed [`Future`] for use in cases where you can't /// statically type your result or need to add some indirection. -pub type BoxFuture<'a, T> = Pin + Send + 'a>>; +pub struct BoxFuture<'a, T>(Pin + Send + 'a>>); #[cfg(feature = "alloc")] +#[must_use = "futures do nothing unless polled"] +#[allow(missing_debug_implementations)] /// `BoxFuture`, but without the `Send` requirement. -pub type LocalBoxFuture<'a, T> = Pin + 'a>>; +pub struct LocalBoxFuture<'a, T>(Pin + 'a>>); /// A `Future` or `TryFuture` which tracks whether or not the underlying future /// should no longer be polled. @@ -81,6 +85,7 @@ impl TryFuture for F #[cfg(feature = "alloc")] mod if_alloc { + use core::mem; use alloc::boxed::Box; use super::*; @@ -96,4 +101,62 @@ mod if_alloc { ::is_terminated(&**self) } } + + impl Future for BoxFuture<'_, O> { + type Output = O; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut_pin = self.0.as_mut(); + mut_pin.poll(cx) + } + } + + impl BoxFuture<'_, O> { + /// Converts Pin> to PinnedFuture + pub fn new<'a>(f: Pin + Send + 'a>>) -> BoxFuture<'a, O> { + BoxFuture(f) + } + } + + impl Future for LocalBoxFuture<'_, O> { + type Output = O; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut_pin = self.0.as_mut(); + mut_pin.poll(cx) + } + } + + impl LocalBoxFuture<'_, O> { + /// Converts Pin> to PinnedFuture + pub fn new<'a>(f: Pin + 'a>>) -> LocalBoxFuture<'a, O> { + LocalBoxFuture(f) + } + } + + unsafe impl<'a, T> UnsafeFutureObj<'a, T> for BoxFuture<'a, T> where T: 'a + { + fn into_raw(mut self) -> *mut (dyn Future + 'a) { + let ptr = unsafe { self.0.as_mut().get_unchecked_mut() as *mut _ }; + mem::forget(self); + ptr + } + + unsafe fn drop(ptr: *mut (dyn Future + 'a)) { + drop(Pin::from(Box::from_raw(ptr))) + } + } + + unsafe impl<'a, T> UnsafeFutureObj<'a, T> for LocalBoxFuture<'a, T> where T: 'a + { + fn into_raw(mut self) -> *mut (dyn Future + 'a) { + let ptr = unsafe { self.0.as_mut().get_unchecked_mut() as *mut _ }; + mem::forget(self); + ptr + } + + unsafe fn drop(ptr: *mut (dyn Future + 'a)) { + drop(Pin::from(Box::from_raw(ptr))) + } + } } diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index f44bfb0cdb..08244d4b14 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -495,7 +495,7 @@ pub trait FutureExt: Future { fn boxed<'a>(self) -> BoxFuture<'a, Self::Output> where Self: Sized + Send + 'a { - Box::pin(self) + BoxFuture::new(Box::pin(self)) } /// Wrap the future in a Box, pinning it. @@ -505,7 +505,7 @@ pub trait FutureExt: Future { fn boxed_local<'a>(self) -> LocalBoxFuture<'a, Self::Output> where Self: Sized + 'a { - Box::pin(self) + LocalBoxFuture::new(Box::pin(self)) } /// Turns a [`Future`](Future) into a