How to call, from Future::poll, a manual subfuture that borrows mutably

Given a future from an async function or async closure/block (hereby called "subfuture"), is it possible to call this from another future implementing poll manually (hereby called "main future"),
assuming that subfuture is borrowing from the main future?

This is trivial in case subfuture is also a manual future with a concrete type instead of unnamable type (like Tokio's Delay example) or if the subfuture doesn't borrow anything.

But in the case below it seems somewhat difficult:

use std::{
    pin::Pin,
    task::{Context, Poll},
};

async fn subfuture(message: &mut String) {
    // some work
}

struct SomeFut<F: Future<Output = ()>> {
    some_subfuture: F,
    message: Option<String>,
}

pub fn some_future(mut message: String) -> impl Future<Output = String> {
    let mut message = Some(message);
    // error, message might not live long enough
    let some_subfuture = subfuture(message.as_mut().unwrap());

    SomeFut {
        some_subfuture,
        message,
    }
}

impl<F: Future<Output = ()>> Future for SomeFut<F> {
    type Output = String;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = unsafe { self.get_unchecked_mut() };
        match unsafe { Pin::new_unchecked(&mut this.some_subfuture) }.poll(cx) {
            Poll::Ready(()) => Poll::Ready(this.message.take().unwrap()),
            Poll::Pending => Poll::Pending,
        }
    }
}

The solution I thought was extending (to static) the lifetime of the message being passed at the subfuture using raw pointer dereferencing, but I imagine that this should be unsound here because message the crosses the function boundary of SomeFut (and MIRI complained as expected).

And I couldn't find a way for the type system to allow me to create the subfuture inside poll instead of at SomeFut creation time, because it is not a concrete type.

Allocating subfuture in a Box should work, but I wanted to avoid that (and having the main future implemented in the conventional way would also avoid allocating the subfuture).

After too much reading with puzzled though, I realised your talking about poll not pool.

It needs unsafe code. Not worth effort when you can just write an async block.

I am aware that this requires unsafe code (it actually already has unsafe code there), and also aware that it can be done in with just async fn easy_future(mut message: String) -> String { subfuture(&mut message).await; message}. This is an exercise to learn more about async, if anyone is interesting in helping, I would highly appreciate.

On nightly, you can use type_alias_impl_trait. Not sure how to workaround this on stable however.

You will either have to box the message String such that moving SomeFut doesn't move the String, or you have to initialize some_subfuture on the first poll call (at which point SomeFut has been pinned).

It seems that:

type MyFuture = impl Future<Output = ()>;
struct HardFut {
    fut: MyFuture,
    message: String,
}

pub fn hard_future(mut message: String) -> impl Future<Output = String> {
    let mut some_other_fut = subfuture(&mut message);
    HardFut {
        fut: some_other_fut,
        message,
    }
}

won't work because it's not considered using a concrete type:

error: unconstrained opaque type
  --> src/main.rs:34:17
   |
34 | type MyFuture = impl Future<Output = ()>;
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `MyFuture` must be used in combination with a concrete type within the same crate

error[E0308]: mismatched types
  --> src/main.rs:43:14
   |
34 | type MyFuture = impl Future<Output = ()>;
   |                 ------------------------ the expected future
...
43 |         fut: some_other_fut,
   |              ^^^^^^^^^^^^^^ expected future, found a different future
   |
   = note: distinct uses of `impl Trait` result in different opaque types

Edit:

A simplified example of this problem:

#![feature(type_alias_impl_trait)]

use std::{
    any::{Any, TypeId},
    fmt::Display,
};

fn typev<T: ?Sized + Any>(_s: &T) -> TypeId {
    TypeId::of::<T>()
}

fn func1() {
    type MyType = impl Display;
    // this okay
    let z: MyType = "".to_string();
    fn func2() -> TypeId {
        let x = "".to_string();
        // uncomment and then this fails to compile:
        // let y: MyType = x;
        typev(&x)
    }
    assert_eq!(typev(&z), func2());
}

fn main() {
    func1();
}

or even simpler, this fails to compile:

#![feature(type_alias_impl_trait)]
use std::fmt::Display;
type MyType = impl Display;
fn main() {
    let z: MyType = "sds".to_string();
}

I don't know if I correctly understood your post, but I want to initialize some_subfuture on the first call to poll, the problem is that the type system doesn't let me keep the future of some_subfuture in the struct; e.g. as Option<impl Future> that is None at struct initialization time and then change it Some(some_subfuture(...)) at when first calling poll. It wants a concrete type at struct initialization, but some_future(...) is unnameable type.

I suppose a solution would be to keep this initial state in some kind of raw pointer, maybe Option<NonNull<u8>> and then cast this to future to the proper type on each call to poll

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.