Cloning a generator/async block

Generators and async blocks don't implement Clone, even if they trivially could:

let wow0 = async {};
let wow1 = Clone::clone(&wow0); // ERROR

Are there any secret, horribly unstable compiler features to enable this? Obviously it's impossible for this to be sound if the generator/async ever holds a self-reference across a yield point, but it would be useful for me if it could work the rest of the time and just give a compile error otherwise.

Edit: To be clear, I want to start a generator, resume it a few times, then clone its current state so I can resume it multiple times from the same state.

Well, for one, you can't always implement clone, and the exact details for when you can would probably be rather complicated. Additionally, you don't want changes in this logic to break existing code, which it very easily could.

The compiler knows what the generated state machine type looks like, so it should be able to tell whether it can implement clone. For the user, I think the rules would be as simple as: all the captured variables need to implement clone, all local variables which exist across a yield need to implement clone, the generator must never hold a self-reference across a yield. I don't see how making this possible would break existing code, since it's just providing new trait impls where they didn't exist and couldn't be manually implemented before.

At this point I'm considering whether it's worth implementing the generator -> state machine translation myself with a macro (though this is impossible to implement reliably if there are macro calls in the generator/async body). Or maybe hacking this into the compiler myself, though I wouldn't want to put in the effort unless I expect it to be merged upstream behind a feature flag.

Edit: I've cross-posted this to IRLO: Implementing Clone for generators/async blocks - compiler - Rust Internals

There's also another problem: it makes it a semver breaking change to use a non-clone type in async code, or to borrow something you didn't previously.

1 Like

That's a rather devious little side-effect.

One of the language's values is that the type system isn't affected by any changes you make to the implementation (i.e. a function/closure's body), meaning all you need to do to use something is look at its signature to reason about something[1].

Having Clone implicitly conditionally derived seems like it would conflict with that value.

  1. As an example of something that doesn't do this C++ lets you write auto as your function's return type and the compiler will automatically deduce the actual return type based on the function body. Therefore to tell what type gets returned you either need to read the function implementation yourself or rely on your IDE to read it for you. ↩ī¸Ž

1 Like

It's already a problem for Send and Sync. In Tokio we have this test that asserts those traits are as we expect for all public async methods:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.