Есть ли другой вариант делить Арку в нескольких замыканиях, кроме клонирования перед каждым закрытием?

У меня есть что-то вроде этого:

use std::sync::Arc;

fn main() {
    let arc = Arc::new(42);
    move || { arc.clone() };
    move || { arc.clone() };
}

Я получаю:

error[E0382]: capture of moved value: `arc`
 --> src/main.rs:6:19
  |
5 |         move || { arc.clone() };
  |         ------- value moved (into closure) here
6 |         move || { arc.clone() };
  |                   ^^^ value captured here after move
  |
  = note: move occurs because `arc` has type `std::sync::Arc<i32>`, which does not implement the `Copy` trait

Я понимаю, почему я получаю это: clone не вызывается раньше arc передается на закрытие. Я могу исправить это, определив каждое замыкание в функции и клонировать Arc перед передачей в закрытие, но есть ли другой вариант?

2 ответа

Решение

Обойти это невозможно. Вы должны клонировать Arc прежде чем он будет использован в закрытии. Общий шаблон заключается в том, чтобы повторно связать клонированный Arc с тем же именем во вложенной области видимости:

use std::sync::Arc;

fn main() {    
    let arc = Arc::new(42);
    {
        let arc = arc.clone();
        move || { /* do something with arc */ };
    }
    {
        let arc = arc.clone();
        move || { /* do something else with arc */ };
    }
}

Обычно это делается вместе с thread::spawn():

use std::sync::{Arc, Mutex};
use std::thread;

const NUM_THREADS: usize = 4;

fn main() {
    let arc = Arc::new(Mutex::new(42));
    for _ in 0..NUM_THREADS {
        let arc = arc.clone();
        thread::spawn(move || {
            let mut shared_data = arc.lock().unwrap();
            *shared_data += 1;
        });
    }
}

есть ли другой вариант?

Поскольку эта модель клонирования вещей до определения замыкания является довольно распространенной, некоторые люди предлагают добавить что-то вроде clone || как аналог move ||, Я бы не стал надеяться на это, но в ряде комментариев указывалось, что макросы могут решить проблему достаточно хорошо.

Несколько ящиков предоставляют некоторую форму этого макроса:

Вполне вероятно, что многие проекты определяют свои собственные макросы, чтобы сделать что-то подобное. Например, пример WASM rust-todomvc определяет:

macro_rules! enclose {
    ( ($( $x:ident ),*) $y:expr ) => {
        {
            $(let $x = $x.clone();)*
            $y
        }
    };
}

Который может быть использован как:

fn main() {
    let arc = Arc::new(42);
    enclose! { (arc) move || arc };
    enclose! { (arc) move || arc };
}
Другие вопросы по тегам