Skip to content

Unexpected partial move in async move block for Copy field #120981

Open
@jwilm

Description

@jwilm

It is possible to write code which looks like it should not compile and does not function as expected when trying to mutate Copy fields on a struct in an async move block.

I tried this code:

use futures::StreamExt;

#[derive(Debug)]
struct Foo { a: i32, b: i32 }

async fn work() -> Foo {
    let mut foo = Foo { a: 0, b: 0};
    let mut futs = futures::stream::FuturesUnordered::new();
    
    for i in 0..100 {
        foo.a += 1;
        
        futs.push(tokio::spawn(async move {
            foo.b += 1;
        }));
    }
    
    for i in 0..100 {
        futs.next().await;
    }
    
    foo
}

#[tokio::main]
async fn main() {
    println!("{:?}", work().await);
}

I expected to see this happen: Foo should have value 100 for both a and b

Instead, this happened: Foo.b is zero.

This behavior is so surprising that it should at least be a warning from rustc. The outcome is clearly not the intent of the code, but it looks like it should work as expected.

Meta

rustc --version --verbose:

rustc 1.76.0 (07dca489a 2024-02-04)

This is possibly a dupe of #108808

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-edition-2021Area: The 2021 editionA-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.C-bugCategory: This is a bug.D-editionDiagnostics: An error or lint that should account for edition differences.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions