Pre-RFC: Move-or-borrow elision

However, in the post, there's also:

Since this design work was in a spirit of direct contradiction to Rust’s goals, it’s hard to see it having a big impact on the design of Rust.

And in the original IRLO thread, he even mentions as well that:

This post is not to propose any change to Rust (even my brief discussion of "Autoclone" at the end I'm not very interested in talking about further now).

So, I'm not sure whether this is a good idea – for multiple reasons – but I figure we might as well discuss it as an option:

What if Clone implementations could have an attribute indicating that the call can be replaced by a move if the object is cloned and then immediately dropped? Then, the initial function could call .clone() consistently (even on the last usage), and still have the code be optimized the way you would hope for.

3 Likes

I'd consider an attribute instead if this is deemed desirable:

fn func() {
  #[autoclone(Rc)] {
    let x = Rc::new(42);
    foo(x);
    bar(x);
    baz(x);
  }
}

The attribute would make the compiler behave in the block as if the types named were copyable, where the copy is performed by calling clone() instead of memcpy. The attribute could be applied to any block, function or item that can have expressions as a descendant.

The advantage of this is that the code looks like code that uses Copy types, and it's clear what code (the clone method on the named types) is being implicitly called, plus you can just apply this to the whole crate if you want a "simpler language".

2 Likes

Doubt that is a good idea since it's even more implicit. Also the block where x is used may not always be packed together, and might be mixed with other stuff.

I proposed something very similar here which had some discussion...

FWIW my desire for this feature mostly stemmed from the pre-async/await days, where I frequently needed to clone things into Future combinator closures, and has mostly gone away since we can now properly borrow into async functions. I'm curious about what use-cases others are seeing the frequent need to clone smart pointers into - maybe there are themes that could drive other solutions?

1 Like

I found a silly way to implement these move or clone.

If we tolerate to rebind the variable in each call and we put a drop::<special_thing>(variable) at the end of the scope, then we can use type inference to write the same in each of the calls. You can see in the example that there are three foo calls, but only occur two clones.

5 Likes

That's a beautiful, terrible hack! I love it.

Pity the rebinding requirement means it can't be used in loops --

-- hang on, the status quo is that you can't optimize the last clone out of a loop anyway, without adding explicit logic.

That might be a small argument in favor of adding something to support this on the language level. It would allow the compiler to (sometimes) optimize away the last clone in a loop, without additional work by the programmer.

In general this would be very hard, because the vast majority of the time there's no way for the compiler to know that a particular iteration of a loop is the last one.

The library codes it manually in a few places, such as this in Vec:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.