Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Which captured variable cannot escape FnMut closure body? #69446

Closed
akshayknarayan opened this issue Feb 24, 2020 · 3 comments · Fixed by #72598
Closed

Which captured variable cannot escape FnMut closure body? #69446

akshayknarayan opened this issue Feb 24, 2020 · 3 comments · Fixed by #72598
Labels
A-async-await A-closures A-diagnostics AsyncAwait-Triaged C-enhancement D-confusing D-newcomer-roadblock P-medium T-compiler

Comments

@akshayknarayan
Copy link

akshayknarayan commented Feb 24, 2020

Related to #31752. cc @jonhoo

This code does not compile (playground):

extern crate futures_util; // 0.3.4
use futures_util::stream::StreamExt;

struct Foo;
impl Foo {
    fn foo(&mut self) { }
}

fn main() {
    let mut x = Foo;
    futures_util::stream::iter(0..1).for_each(move |_| {
        async { x.foo(); }
    });
}

due to:

error: captured variable cannot escape `FnMut` closure body
  --> src/main.rs:12:9
   |
11 |     futures_util::stream::iter(0..1).for_each(move |_| {
   |                                                      - inferred to be a `FnMut` closure
12 |         async { x.foo(); }
   |         ^^^^^^^^^^^^^^^^^^ returns a reference to a captured variable which escapes the closure body
   |
   = note: `FnMut` closures only have access to their captured variables while they are executing...
   = note: ...therefore, they cannot allow references to captured variables to escape

It would be helpful if the compiler said which captured variable escaped the closure (in the example, it is x, but this is not always clear if there are many captured variables).

@jonas-schievink jonas-schievink added A-async-await A-closures A-diagnostics C-enhancement T-compiler labels Feb 24, 2020
@estebank estebank added D-confusing D-newcomer-roadblock labels Feb 25, 2020
@tmandry tmandry added AsyncAwait-Triaged P-medium labels Feb 25, 2020
@csmoe
Copy link
Member

csmoe commented Feb 26, 2020

minimized without futures-util crate(playground):

use core::future::Future;

struct Foo;
impl Foo {
    fn foo(&mut self) {}
}

async fn bar<T>(x: impl FnMut() -> T)
where
    T: Future<Output = ()>,
{
}
fn main() {
    let mut x = Foo;
    bar(move || async {
        x.foo();
    });
}

@frantisekhanzlikbl

This comment has been minimized.

@frantisekhanzlikbl
Copy link

frantisekhanzlikbl commented Apr 25, 2020

Nevermind, I figured out what was wrong.

If I were able to define an async closure mutably capturing its environment, it would be possible to invoke the closure multiple times without actually awaiting the future (or dropping it in some other way).
This way, we would get multiple Futures with aliased mutable pointers.

If it is known that this won't happen, then a combination of Rc and RefCell can be used to achieve a safe solution to this problem:

let x = Rc::new(RefCell::new(0));
(async || *(x.borrow_mut()) += 1)().await;

Aaron1011 added a commit to Aaron1011/rust that referenced this issue May 26, 2020
Fixes rust-lang#69446

When we encounter a region error involving an `FnMut` closure, we
display a specialized error message. However, we currently do not
tell the user which upvar was captured. This makes it difficult to
determine the cause of the error, especially when the closure is large.

This commit records marks constraints involving closure upvars
with `ConstraintCategory::ClosureUpvar`. When we decide to 'blame'
a `ConstraintCategory::Return`, we additionall store
the captured upvar if we found a `ConstraintCategory::ClosureUpvar` in
the path.

When generating an error message, we point to relevant spans if we have
closure upvar information available. We further customize the message if
an `async` closure is being returned, to make it clear that the captured
variable is being returned indirectly.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Jun 13, 2020
…n, r=nikomatsakis

Display information about captured variable in `FnMut` error

Fixes rust-lang#69446

When we encounter a region error involving an `FnMut` closure, we
display a specialized error message. However, we currently do not
tell the user which upvar was captured. This makes it difficult to
determine the cause of the error, especially when the closure is large.

This commit records marks constraints involving closure upvars
with `ConstraintCategory::ClosureUpvar`. When we decide to 'blame'
a `ConstraintCategory::Return`, we additionall store
the captured upvar if we found a `ConstraintCategory::ClosureUpvar` in
the path.

When generating an error message, we point to relevant spans if we have
closure upvar information available. We further customize the message if
an `async` closure is being returned, to make it clear that the captured
variable is being returned indirectly.
RalfJung added a commit to RalfJung/rust that referenced this issue Jun 13, 2020
…n, r=nikomatsakis

Display information about captured variable in `FnMut` error

Fixes rust-lang#69446

When we encounter a region error involving an `FnMut` closure, we
display a specialized error message. However, we currently do not
tell the user which upvar was captured. This makes it difficult to
determine the cause of the error, especially when the closure is large.

This commit records marks constraints involving closure upvars
with `ConstraintCategory::ClosureUpvar`. When we decide to 'blame'
a `ConstraintCategory::Return`, we additionall store
the captured upvar if we found a `ConstraintCategory::ClosureUpvar` in
the path.

When generating an error message, we point to relevant spans if we have
closure upvar information available. We further customize the message if
an `async` closure is being returned, to make it clear that the captured
variable is being returned indirectly.
RalfJung added a commit to RalfJung/rust that referenced this issue Jun 13, 2020
…n, r=nikomatsakis

Display information about captured variable in `FnMut` error

Fixes rust-lang#69446

When we encounter a region error involving an `FnMut` closure, we
display a specialized error message. However, we currently do not
tell the user which upvar was captured. This makes it difficult to
determine the cause of the error, especially when the closure is large.

This commit records marks constraints involving closure upvars
with `ConstraintCategory::ClosureUpvar`. When we decide to 'blame'
a `ConstraintCategory::Return`, we additionall store
the captured upvar if we found a `ConstraintCategory::ClosureUpvar` in
the path.

When generating an error message, we point to relevant spans if we have
closure upvar information available. We further customize the message if
an `async` closure is being returned, to make it clear that the captured
variable is being returned indirectly.
RalfJung added a commit to RalfJung/rust that referenced this issue Jun 13, 2020
…n, r=nikomatsakis

Display information about captured variable in `FnMut` error

Fixes rust-lang#69446

When we encounter a region error involving an `FnMut` closure, we
display a specialized error message. However, we currently do not
tell the user which upvar was captured. This makes it difficult to
determine the cause of the error, especially when the closure is large.

This commit records marks constraints involving closure upvars
with `ConstraintCategory::ClosureUpvar`. When we decide to 'blame'
a `ConstraintCategory::Return`, we additionall store
the captured upvar if we found a `ConstraintCategory::ClosureUpvar` in
the path.

When generating an error message, we point to relevant spans if we have
closure upvar information available. We further customize the message if
an `async` closure is being returned, to make it clear that the captured
variable is being returned indirectly.
RalfJung added a commit to RalfJung/rust that referenced this issue Jun 15, 2020
…n, r=nikomatsakis

Display information about captured variable in `FnMut` error

Fixes rust-lang#69446

When we encounter a region error involving an `FnMut` closure, we
display a specialized error message. However, we currently do not
tell the user which upvar was captured. This makes it difficult to
determine the cause of the error, especially when the closure is large.

This commit records marks constraints involving closure upvars
with `ConstraintCategory::ClosureUpvar`. When we decide to 'blame'
a `ConstraintCategory::Return`, we additionall store
the captured upvar if we found a `ConstraintCategory::ClosureUpvar` in
the path.

When generating an error message, we point to relevant spans if we have
closure upvar information available. We further customize the message if
an `async` closure is being returned, to make it clear that the captured
variable is being returned indirectly.
@bors bors closed this as completed in 9cee22c Jun 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await A-closures A-diagnostics AsyncAwait-Triaged C-enhancement D-confusing D-newcomer-roadblock P-medium T-compiler
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants