bumpalo icon indicating copy to clipboard operation
bumpalo copied to clipboard

Bumpalo's Box does not seem to work with dyn Future

Open rw opened this issue 3 years ago • 4 comments

To reduce memory allocations, I'm trying to use bumpalo to create my own Boxed futures type.

I'm basing this off of an idiom present in the futures crate:

type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a + Send>>;

https://docs.rs/futures/0.3.5/futures/future/type.BoxFuture.html

When I try this with bumpalo::boxed::Box, however, it seems that I can't convert an impl Future into a dyn Future the same way that I can with std::boxed::Box.

Here is some example code demonstrating the issue (I wish the playground had bumpalo!):

use std::future::Future;
use std::pin::Pin;

/// This compiles:
fn f(
    fut: impl Future<Output = ()> + Send + 'static,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
    Box::pin(fut)
}

// This does not compile:
// fn g<'a>(
//     b: &'a bumpalo::Bump,
//     fut: impl Future<Output = ()> + Send + 'static,
// ) -> Pin<bumpalo::boxed::Box<'a, dyn Future<Output = ()> + Send + 'static>> {
//     bumpalo::boxed::Box::pin_in(fut, &b)
// }

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=80eeec8261dc28169cfc968525ed7e43

Is there something I'm missing with how to use this library? Thank you!

rw avatar Aug 15 '20 03:08 rw

I think that the standard library Box is special cased by the language here.

You should be able to create a &mut dyn Future from a &mut MyFuture that was allocated in a bump.

And if we had a public, unsafe constructor for bumpalo::boxed::Box that took a &mut then you could do

  • allocate a MyFuture in the bump, resulting in a &mut MyFuture
  • convert &mut MyFuture to &mut dyn Future
  • unsafely construct a bumpalo::boxed::Box from the &mut dyn Future

You would probably want to wrap that up in a method (unfortunately, I don't think the type system can express this generically, so we can't do it in this crate).

I think an unsafe constructor is reasonable to support, so if you want to open a PR, I can review+merge it.

fitzgen avatar Aug 17 '20 21:08 fitzgen

@fitzgen Would the function look like this?

impl<'a, T: ?Sized> Box<'a, T> {
    #[inline]
    pub unsafe fn from_mut(x: &mut T) -> Self {
        Box(x)
    }

or

impl<'a, T: ?Sized> Box<'a, T> {
    #[inline]
    pub unsafe fn from_mut(x: &mut T) -> Self {
        Box(&mut *x)
    }

rw avatar Aug 29 '20 21:08 rw

It would look like the first, but additionally the parameter would use the 'a lifetime: x: &'a mut T

fitzgen avatar Sep 01 '20 16:09 fitzgen

std::boxed::Box implements CoerceUnsized which is a nightly-only trait for doing this. You can fake it with unsafe { BumpaloBox::from_raw(BumpaloBox::into_raw(node) as *mut dyn Whatever) }, or by the above additional function...

However it doesn't work too well. The fact that Box is just a wrapper for a &'a mut T causes a lot of issues with trait object invariance. You need to use raw pointers like the real Box to make it function properly.

ColonelThirtyTwo avatar Oct 22 '20 00:10 ColonelThirtyTwo