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).
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.
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).
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