Rfcs: Officially implement a clone macro

Created on 7 Jun 2017  ·  19Comments  ·  Source: rust-lang/rfcs

At the bottom of http://gtk-rs.org/tuto/closures, they gave us this

macro_rules! clone {
    (@param _) => ( _ );
    (@param $x:ident) => ( $x );
    ($($n:ident),+ => move || $body:expr) => (
        {
            $( let $n = $n.clone(); )+
            move || $body
        }
    );
    ($($n:ident),+ => move |$($p:tt),+| $body:expr) => (
        {
            $( let $n = $n.clone(); )+
            move |$(clone!(@param $p),)+| $body
        }
    );
}

which would allow more seamless cloning.

let windows_clone = windows.clone();
button.connect_clicked(move |_| {

would become

button.connect_clicked(clone!(windows => move |_| {

Why is this good?

  1. Less messy
  2. No reason to think of an unique variable name every time

And I just thought having some official solution to this, whether it's as simple as copying the macro code, or if it's making some new syntax, it would be awesome to have :)

T-libs

Most helpful comment

FWIW, there's the enclose crate that does something very similar to this, which I use for my gtk-rs app now: https://crates.io/crates/enclose

All 19 comments

New macros require RFCs, because they are insta-stable and globally visible. Please pursue this that way, thank you!

You mean I should make a pull request? Sorry, I'm a beginner regarding this.

Whoah sorry, I was confused as to what repo I was in; what you did is 100% fine. My bad! I thought you had filed this against rust-lang/rust 😓

Oh, haha. No worries :)

Would adding a Clone implementation for closures also fix this problem?

I don't think so. The macro doesn't clone the closure - it clones the specified arguments in the closure. It's sugar for writing

let thingy_clone = thingy.clone();
closure(move |test| {
    thingy_clone.doStuff(test);
});

Because, as you can see, that gets quite messy.
This macro allows you to do that without temporary variables - see the example I posted.

ah yea, too bad macros can't implement traits.

Wait what? How are traits related?

I'd personally perfer blah.clone() but your macro seems like a good addition considering it isn't really possible to do it as a method.

@nixpulvis Macros can implement traits I use it in my library extensively with this macro. On top of that procedural macros can be created for auto derivation of traits.

@nixpulvis

  1. Not my macro.

At the bottom of http://gtk-rs.org/tuto/closures, they gave us this

  1. What exactly is blah in this context?

I think some of what I have posted might be good food for thought, but within the context of this RFC proposal probably irrelevant.

I don't even know what a macro implementing a trait means (though it sounds cool), I don't really know how many compiler tricks would be needed to implement Clone for closures (I'm guessing > 0). This macro seems mostly harmless, although it's just another case of needed to know to use a macro as opposed to Rust's more natural form of abstraction.

@legolord208

  1. Hopefully nobody here cares too much about attribution (I sure don't).
  2. blah would be something you want to clone, so in my head something implementing the Clone trait... which should cover closures I guess.

I didn't mean to actually clone a closure. I meant to clone variables used inside a move closure.

This seems like a good idea. C++'s closures let you explicitly capture specific variables by copy or reference; since Rust is expressive enough to express this as a macro, we might as well do so. Given some of the discussion above, though, I think that clone! might be the wrong name for it; possibly capture_cloned! or capture_by_copy! would be more self-documenting.

I've read through some of the gtk-rs examples which use this macro and found it very hard to read. The explicit let v = v.clone(); some_func(move || ...) is far more readable and it is more obvious where cloning is required and which variables get cloned.

I'm not sure, whether the Rust language would really benefit from such a macro.

let v = v.clone() is unnecessarily verbose and not practical in many cases.

let v = ...;
let u = ...;
{  // unnecessary block
    let v = v.clone();
    let u = u.clone();  // this can be error-prone, ...
    some_func(move || {
        drop(v);
        drop(u); // ...if you do similar things to `v` and `u` and they are of the same type
     });
}
v.method1();
u.method2();

I agree that this macro might not be the best solution, but is useful.

FWIW, there's the enclose crate that does something very similar to this, which I use for my gtk-rs app now: https://crates.io/crates/enclose

About one year later I had a lot of use-cases where the macro would have been handy and the code would also be cleaner. Therefore I have changed my mind and propose the inclusion of a clone-like macro into the standard library.

FWIW, gtk-rs plans on making such a macro too: https://github.com/gtk-rs/gtk/issues/290

Was this page helpful?
0 / 5 - 0 ratings

Related issues

onelson picture onelson  ·  3Comments

3442853561 picture 3442853561  ·  3Comments

mahkoh picture mahkoh  ·  3Comments

3442853561 picture 3442853561  ·  3Comments

3442853561 picture 3442853561  ·  3Comments