Can I avoid cloning?

This below works, but...

fn some(cond_var_skip: Arc<(Mutex<bool>, Condvar)>)
{
....later creating some dialog:
Dialog::new()...blablabla...
.button("Overwrite", |s| {
       call(&cond_var_skip);
    })
}

If I want to add another "button" << this is really irrelevant and I put:

Dialog::new()...blablabla...
.button("Overwrite", |s| {
       call(cond_var_skip);
    })
.button("Skip", |s| {
       call(cond_var_skip);//<<Here I will get error that the value is being used after move
    })

I of course found "workaround" which basically does this:

let cond_var_skip_clone = cond_var.clone();

but this^^^ means that for each new button, I have to create another clone, which I find extremely unsatisfactory. Is there a way to avoid cloning and simply pass that Arc by reference?

Thank you

Cloning an Arc is incredibly cheap, since it reuses the underlying object. I really would not try to minimize clones of an Arc.

1 Like

Hi, it is not about the cost really, it is about the clutter in code...
Especially, that logically, the options that is pressing button Overwrite or Skip and thus running that closure are exclusive. If I run one I cannot run the other, co cloning is rather non-intuitive here and tbh, pointless.
I don't understand the fact that I cannot simply pass that Arc by reference and be done with it.

I mean, you can take a reference to an Arc.

fn some(cond_var_skip: &Arc<(Mutex<bool>, Condvar)>) {
    ...
}

I've tried that, I was getting the same error, saying that the value doesn't live long enough...
Basically the sit is this:
fn another_fn()
{
//here I'm creating those Arc'ssss
//and passing them to that some(arc)
//then in some() again, I'm passing them to couple of other() in closures
//But here I wasn't able to avoid clonning^^^
}

I am guessing it is because each closure needs its own clone.

let cond_var_skip1 = cond_var_skip.clone();
let cond_var_skip2 = cond_var_skip.clone();
Dialog::new()
    .button("Overwrite", move |s| {
       call(&cond_var_skip1);
    })
    .button("Skip", move |s| {
       call(&cond_var_skip2);
    })

My guess is that the closures are required to be 'static, which means that they must have ownership of any Arc they want to access.

Yes, that is correct, but logically they cannot outlive the "top" function from where they are called. Because the Dialog need to be dealt with and so it will execute "some" closure, but one only at a time.

Exactly^^^^

There is a somewhat fancy way to restructure that:

Dialog::new()
    .button("Overwrite", {
        let cond_var_skip = cond_var_skip.clone();
        move |s| {
            call(&cond_var_skip);
        }
    })
    .button("Skip", {
        let cond_var_skip = cond_var_skip.clone();
        move |s| {
            call(&cond_var_skip);
        }
    })

but ultimately you have to make a separete clone for each closure.

1 Like

I was just wondering if creating each of those closures "on demand" somehow, would solve that problem.

I'm not sure what you mean by on-demand, but you can't avoid the clone.

Thanks for the heads up :wink:

hm.. maybe one can pretty this up with some kind of macro?

E.g. this macro

macro_rules! with_clones {
    ($($capture:ident),+; $e:expr) => {
        {
        $(
            let $capture = $capture.clone();
        )+
            $e
        }
    }
}

and then use it something like this

Dialog::new()...blablabla...
.button("Overwrite", with_clones!(cond_var_skip; |s| {
       call(cond_var_skip);
    }))
.button("Skip", |s| {
       call(cond_var_skip);//<<Here I will get error that the value is being used after move
    })

Or in the call-taking-&Arc<..> version, to rewrite

Dialog::new()
    .button("Overwrite", {
        let cond_var_skip = cond_var_skip.clone();
        move |s| {
            call(&cond_var_skip);
        }
    })
    .button("Skip", {
        let cond_var_skip = cond_var_skip.clone();
        move |s| {
            call(&cond_var_skip);
        }
    })

into

Dialog::new()
    .button("Overwrite", with_clones!(cond_var_skip; move |s| {
        call(&cond_var_skip);
    }))
    .button("Skip", with_clones!(cond_var_skip; move |s| {
        call(&cond_var_skip);
    }))

Makes me wonder if a convenience macro like this already exists. Of course one can also argue that such a macro might decrease readability.

2 Likes

I really like this approach! Thanks

Why doesn't this work:

fn call_clone(arc: &Arc<u32>) -> impl Fn(String) -> u8 {
    let cloned = arc.clone();
    move |s| {
        call(*cloned)
    }
}
Dialog::new()
    .button("Overwrite", call_clone(&cond_var_skip))
    .button("Skip", call_clone(&cond_var_skip))

Rust Playgound

1 Like

Hey! Big thanks, that indeed works as expected. I still have so much to learn.

You're welcome, that is what this forum is for :slight_smile:

I somewhat had something similar in mind when I was mentioning earlier on "creating closures on demand", but in the rust book I found only returning Box with Fn and this somewhat is still too advanced for me... Non-the-less, getting there, and once again, thanks!

Often people prefer to write Arc::clone(x) instead on x.clone(). This clarifies the inherent: you are not cloning x, you are cloning the ownership of x. In your example every button needs to own x, so it is not clutter to include in your code the fact that you creating multiple owners of x, even if only one of them is ever going tio actively use that ownership.

1 Like

I wouldn't say you are cloning the ownership of the value. Arc is a reference, it doesn't really have ownership of the value. Of course the memory is freed once the last Arc to it is destroyed, due to reference counting, but when you call arc.clone() or Arc::clone(arc), you are still only cloning the reference to the value.