Есть ли способ получить замыкание в Rust, в которое помещаются только некоторые переменные? - PullRequest
3 голосов
/ 19 октября 2019

Чтобы не попасть в проблему XY, вот что я пытаюсь сделать:

  1. У меня есть
    • общее struct с настройками и
    • дополнительная переменная настройка, с которой я хочу настроить и поиграть.
  2. Для всех возможных значений в целочисленном диапазоне я хочу начать (scoped) поток с этимпеременная установлена ​​в это значение. (В зависимости от этого значения они выполняют немного различную работу).
  3. Однако каждый из этих потоков должен иметь возможность читать общую структуру настроек.

В псевдо-Rust:

extern crate crossbeam;

struct Settings {
  // ... many fields
}

fn main() {
  crossbeam::scope(|scope| {
    for score in 0..max_feasible_score {
      scope.spawn(|_| {
         let work_result = do_cool_computation(&settings, score);
         println!(work_result);
      }
    }
  }).unwrap();
}

Теперь это не компилируется. Rust жалуется на то, что score заимствовано в замыкании, которое может пережить его внешнюю функцию, и предлагает изменить аргумент scope.spawn замыкания, чтобы преобразовать его в замыкание move || {...}.

Однако это сделает недействительным &settings, поскольку первая итерация цикла переходит во владение settings в закрытие хода.

Единственный простой способ заставить его работать, это, например, скопировать структуру Settings в каждый поток (который в моемреальное приложение довольно дорогое), или представьте дугу около settings, что также кажется немного неудачным.

Есть ли способ, которым мы можем обойти подсчет ссылок здесь? Есть ли способ, которым мы можем переместиться score во внутреннее закрытие, при этом все еще разрешено ссылаться на settings?

1 Ответ

4 голосов
/ 19 октября 2019

Я нашел ответ в документации по rayon :: scope , который, как оказалось, как раз об этой проблеме: «Доступ к данным стека [изнутри области потоков с областями действия]». На этой странице также есть пример, который более ясен, чем псевдокод в этом вопросе.

Оказывается, что вы можете: - Использовать замыкание перемещения, но ссылаться на переменные во внешней области, скрывая их ссылкой;поэтому захватывая их по ссылке, а не по значению, используя let settings = &settings:

fn main() {
  crossbeam::scope(|scope| {
    let settings = &settings; // refer to outer variable by reference
    for score in 0..max_feasible_score {
      scope.spawn(move |_| {
         let work_result = do_cool_computation(&settings, score);
         println!(work_result);
      }
    }
  }).unwrap();
}
  • Используйте обычное замыкание и перемещайте только необходимые переменные, затеняя их внутри замыкания, используя let score = score:
fn main() {
  crossbeam::scope(|scope| {
    for score in 0..max_feasible_score {
      scope.spawn(|_| {
         let score = score; // capture only score 
         let work_result = do_cool_computation(&settings, score);
         println!(work_result);
      }
    }
  }).unwrap();
}

Итак:

  • Да, в замыкание можно переместить только одну или несколько переменных (а не все или ни одного).
  • Да, это можно использовать для «обхода» подсчета ссылок.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...