Futures::select macro and compiler lifetime complaints

Hello,

I have a struct Foo:

struct Foo {}

impl Foo {
    async fn do_foo(&self) -> String {
        "did foo".to_string()
    }
}

and a function that creates and returns a Foo, but before returning it, calls the async function do_foo.

Everything works as expected when awaiting the future returned by do_foo:

async fn compiler_success() -> Foo {
    let foo = Foo {};

    let foo_f = Box::pin(foo.do_foo().fuse());

    let foo_val = foo_f.await;
    println!("{foo_val}");

    foo
}

However, if I use futures::select macro instead of awaiting:

async fn compiler_failure() -> Foo {
    let foo = Foo {};

    let mut foo_f = Box::pin(foo.do_foo().fuse());

    select! {
        foo_val = foo_f => println!("{foo_val}"),
        complete => println!("Complete"),
        default => println!("Default"),
    };

    foo
}

the compiler complains about lifetimes:

error[E0505]: cannot move out of foo because it is borrowed

From what I understand the compiler cannot be sure that indeed foo is returned only when the future has completed.

I want to use the select! macro because what I actually try to achieve is slightly more complex: awaiting more than one futures in a loop until getting a value from do_foo future.

Please note that Foo cannot implement Clone.

Is there a way to achieve what I want? I don't have problem using some other way instead of select!.

Thanks!

Could you write a minimum reproduction case? When I copy-paste this code into the Playground, it gives some unrelated errors (do_foo().fuse() does not work because String is not an iterator, and the select! usage has DSL grammatical errors). When I fix these errors, it compiles:

So, I am unable to reproduce the error you see. There is not much advice I can provide.

1 Like

Please use futures::select and bring the FutureExt in scope.

Here is the code in playground.

1 Like

I actually just figured out a way, but it feels more like a hack to get around the compiler:

use futures::{channel::oneshot::channel, select, FutureExt};

async fn compiler_failure() -> Foo {
    let foo = Foo {};
    let (tx, rx) = channel::<Foo>();

    let mut tuple_future = Box::pin(async move {
        let s = foo.do_foo().await;
        (s, foo)
    }).fuse();

    select! {
        (foo_val, foo_struct) = tuple_future => {
            println!("{foo_val}");
            tx.send(foo_struct).unwrap();
        },
        complete => println!("Complete"),
        default => println!("Default"),
    };

    rx.await.unwrap()
}

Anyone with something smarter / simpler?

You should be able to solve this by running drop(foo_f) before returning foo.

1 Like

That's it! Thanks