Макрос за полуавтоматично клониране

Когато правите closure, той се опитва да "прихване" колкото може по-малко от околните променливи, и затова хваща references. Можете да използвате move, за да го накарате да вземе ownership, но е малко по-тегаво, когато имате клонируеми неща:

let logger = BufferedLogger::new(io::stdout(), 100);
let logger_clone  = logger.clone();
do_something_with(move || {
    logger_clone.log("A bit inconvenient");
});

Така създаваме клонинг, и после го подаваме, но името е малко тъпо. В контекста на този closure, няма смисъл да се казва logger_clone, но вече имаме logger... Може да добавим нов scope:

let logger = BufferedLogger::new(io::stdout(), 100);
do_something_with({
    let logger = logger.clone();
    move || {
        logger.log("A tad over-nested");
    }
});

Това работи, защото {} блока ще създаде нов scope за имена. Но пък добавя ново ниво на индентация. За щастие, процеса е достатъчно лесен за автоматизация с макроси:

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
        }
    );
}

Сега можем да направим нещо такова:

let logger = BufferedLogger::new(io::stdout(), 100);
let widget = Widget::new();
do_something_with(clone!(logger, widget => move || {
    logger.log("Nice!");
    widget.fidget();
});

Има и други начини да изразите същото нещо с макроси -- важна е идеята. Този конкретен метод е взет от сайта на gtk-rs: https://gtk-rs.org/tuto/closures.