Макрос за полуавтоматично клониране
Когато правите 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.