Automatic clones in async code

Hi,

I have a feelthing that the answere will be "No, its not possible", but since its turning out to be quite a issue for the readability of code I wanted to double check that there is no pattern I have missed.

I.n async code (which tends to polute quite a bit, so ends up in a larger part of the codebase when parts of it will be served from a http server) I end up with quite a few structs that are Arc-ified, I.e stuff like

Arc<FunctionalityProvider1>
Arc<FunctionalityProvider2>
etc.

The "issue" is that they are Clone only, not copy, so whenever I want to spawn tasks / async context to operations on them I end up with lots (and lots) of .clone() all over the codebase.

Here I think it "hides" that some clones might actually be expensive, i.e. the code becomes so full of .clone() on Arc that "realy are not that expensive" so that one stop to reacts to seeing .clone().

I really do like that Rust make clones explicit in general - helps me avoid unneeded work - but in async code they are all over the place.

Does anyone have any good pattern to "avoid" .clone()'s like for example:

{
   let a = a.clone();
   let b = b.clone();
   let c = c.clone();
   let d = d.clone();
   spawn(async move { /* use them */});


}

There currently isn't an alternative to this in stable rust. However, there is ongoing work to solve this issue. See the following links for info.

1 Like

as far as I know, the explicit .clone() is unavoidable, not with the current design of the Copy/Clone.

currently, the Copy trait also requires the type does not have a destructor, thus any resource managing type (such as smart pointers) cannot be Copy.

to be able to express "a cheap clonable and should be automatically clonable type", we need new designs. but what should the new mechanism look like? there's no consensus yet.

I've read about ideas of a Claim trait and ergonomic "handle" types, but I don't know how it will turn out.

Keep in mind that this problem is harder that it looks. People like to pretend that Arc::clone costs “nothing”, but in reality it's still does as much work as 0.5KB or maybe even 1KB memcpy,

Add 10 of these together — and you are pretty damn near cost of “heavy” clone. Mean that eliminating them entirely would just make you do the opposite mistake.

Your issue is distincly different from CRUD app developers problem of “who caress about efficiency when we couldn't even understand how to fullfil business requests because business owner don't know what they want?”

1 Like

I see your point, but respectfully disagree. I agree that it would be heavy in a tight loop sens, but not heavy in "im launching a new async task" sense. It makes rust async e.g. web code, harder to reason about, isntead of easier which typically is the case.

(But I see the other links provided, going through them now)

That's what makes it hard. You want to see it in some places (where you are doing actual computations) but not in the other places (where you launch tasks).

That's what makes it harder that CRUD developers dilemma.

Thanks for those links,

Basically I conclude with "Its a known problem, but not really close to a solution as of yet"