rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.25k stars 12.71k forks source link

async/await: cannot move out of captured variable in an `Fn` closure #59971

Open leinlawun opened 5 years ago

leinlawun commented 5 years ago

Hello, recently faced with strange behavior, please comment, is this a bug, or not? Compiler version: 1.35.0-nightly 2019-04-12 99da733f7f38ce8fe684

#![feature(futures_api, async_await, await_macro)]

use std::future::Future;

struct Task<T> {
    task: T,
}

impl<T> Task<T>
where
    T: Fn(),
{
    fn new(task: T) -> Self {
        Self { task }
    }

    fn execute(&self) {
        (self.task)();
    }
}

struct AsyncTask<T> {
    task: T,
}

impl<T, F> AsyncTask<T>
where
    T: Fn() -> F,
    F: Future<Output = ()>,
{
    fn new(task: T) -> Self {
        Self { task }
    }

    async fn execute(&self) {
        await!((self.task)());
    }
}

fn main() {
    let string = "Hello, World!".to_string();
    let _task = Task::new(move || {
        println!("{}", string);
    });

    let string = "Hello, World!".to_string();
    let _async_task = AsyncTask::new(async move || {
        println!("{}", string);
    });
}

(Playground) (Updated Playground

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0507]: cannot move out of captured variable in an `Fn` closure
  --> src/main.rs:47:52
   |
46 |       let string = "Hello, World!".to_string();
   |           ------ captured outer variable
47 |       let _async_task = AsyncTask::new(async move || {
   |  ____________________________________________________^
48 | |         println!("{}", string);
49 | |     });
   | |_____^ cannot move out of captured variable in an `Fn` closure

error: aborting due to previous errors

For more information about this error, try `rustc --explain E0507`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.
nikomatsakis commented 5 years ago

Marking as deferred -- async-await closures are not part of the core async fn functionality we expect to stabilize.

@leinlawun -- I'm not sure precisely what the problem is here, but in general async closures don't really work correctly.

compiler-errors commented 8 months ago

This is expressible now with reworked async closures (#120361) and async Fn() bounds:

#![feature(async_closure)]
#![allow(unused)]

struct Task<T> {
    task: T,
}

impl<T> Task<T>
where
    T: Fn(),
{
    fn new(task: T) -> Self {
        Self { task }
    }

    fn execute(&self) {
        (self.task)();
    }
}

struct AsyncTask<T> {
    task: T,
}

impl<T> AsyncTask<T>
where
    T: async Fn(),
{
    fn new(task: T) -> Self {
        Self { task }
    }

    async fn execute(&self) {
        (self.task)().await;
    }
}

fn main() {
    let string = "Hello, World!".to_string();
    let _task = Task::new(move || {
        println!("{}", string);
    });

    let string = "Hello, World!".to_string();
    let _async_task = AsyncTask::new(async move || {
        println!("{}", string);
    });
}
dev-ardi commented 5 months ago

Shouldn't this be closed then?

compiler-errors commented 5 months ago

@dev-ardi: No. Async closures are not stable.

dev-ardi commented 5 months ago

Is that how it works? If an issue in stable 1.78 is fixed in release 1.80 when is the issue closed? When the fixing PR is closed, at the beta release or when the fix becomes stable?