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 Area: Async & Await A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@akshayknarayan
Copy link

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 Area: Async & Await A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 24, 2020
@estebank estebank added D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. labels Feb 25, 2020
@tmandry tmandry added AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. P-medium Medium priority 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;

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 Area: Async & Await A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants