Запуск параллельных задач для каждого элемента Vec

У меня есть Vec, содержащий данные персонажа, и я хочу одновременно запускать AI персонажа для каждого персонажа. Это происходит не из соображений производительности, а потому, что у каждого ИИ есть периоды восстановления, в течение которых он спит, позволяя другим ИИ работать.

ИИ разрешено изменять CharacterState, но только тот, которому он был назначен. Поэтому теоретически каждый ИИ имеет доступ к отдельной части Vec, и не будет множественных ссылок ни на один CharacterState.

Однако я изо всех сил пытаюсь выразить это в ржавчине. Как мне:

  1. Разделите Vec на несколько изменяемых ссылок, по одной для каждого элемента.
  2. Создайте задачу для каждого предмета.
  3. Ждите всех полученных заданий.

Вот что у меня есть на данный момент: Rust Playground

#[derive(Clone, Default)]
struct Actor<TState>(TState);

impl<TState> Actor<TState> {
    pub fn new(initial_state: TState) -> Self {
        Actor(initial_state)
    }

    pub async fn act(&mut self, action: &(impl Action<State = TState> + ?Sized)) {
        action.execute(&mut self.0).await;
    }
}

#[async_trait::async_trait]
trait Action: Send + Sync {
    type State;

    async fn execute(&self, state: &mut Self::State);
}

struct CharacterData {
    x: i32,
    y: i32
}

type CharacterActor = Actor<CharacterData>;

struct RunCharacterAction;
#[async_trait::async_trait]
impl Action for RunCharacterAction {
    type State = CharacterData;
    
    async fn execute(&self, _state: &mut Self::State) {
        todo!()
    }
}

struct WorldData {
    characters: Vec<CharacterActor>
}

type WorldActor = Actor<WorldData>;

struct RunWorldAction;

#[async_trait::async_trait]
impl Action for RunWorldAction {
    type State = WorldData;
    
    async fn execute(&self, state: &mut Self::State) {
        let tasks: Vec<_> = state.characters
            .iter_mut()
            .map(|character| {
                tokio::spawn(async {
                    let action = RunCharacterAction;
                    character.act(&action).await;
                })
            }).collect();
            
        futures::future::join_all(tasks).await;
    }
}

#[tokio::main]
async fn main() {
    let mut world = WorldActor::new(WorldData {
        characters: vec![
            CharacterActor::new(CharacterData { x: 1, y: 2 }),
            CharacterActor::new(CharacterData { x: 3, y: 4 })]
    });
    
    let action = RunWorldAction;
    world.act(&action).await;
}

Что приводит к следующей ошибке

error[E0521]: borrowed data escapes outside of method
  --> src/main.rs:51:29
   |
50 |       async fn execute(&self, state: &mut Self::State) {
   |                               -----  - lifetime `'life1` defined here
   |                               |
   |                               `state` is a reference that is only valid in the method body
51 |           let tasks: Vec<_> = state.characters
   |  _____________________________^
52 | |             .iter_mut()
   | |                       ^
   | |                       |
   | |_______________________`state` escapes the method body here
   |                         argument requires that `'life1` must outlive `'static`

For more information about this error, try `rustc --explain E0521`.
error: could not compile `playground` (bin "playground") due to 1 previous error

Вероятно, проще всего использовать Vec с чем-то вроде into_iter(), превратить каждый элемент в задачу, затем дождаться всех и собрать в другой Vec.

BallpointBen 06.08.2024 22:19

@BallpointBen Верно, но я не хочу потреблять Vec, если это возможно

Rick de Water 06.08.2024 22:23

Это должно быть возможно с iter_mut(). Что вы пробовали?

PitaJ 06.08.2024 22:25

И join_all().

Chayim Friedman 06.08.2024 22:34

Я добавил пример игровой площадки того, что у меня есть на данный момент.

Rick de Water 06.08.2024 22:51

Пожалуйста, вставьте код в вопрос как Минимально воспроизводимый пример.

Chayim Friedman 06.08.2024 22:52
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
6
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку вам не нужно, чтобы задачи выполнялись параллельно, вам не нужно их создавать. join_all достаточно, чтобы опросить их все одновременно.

let tasks = state
    .characters
    .iter_mut()
    .map(|character| async move {
        let action = RunCharacterAction;
        character.act(&action).await;
    });

futures::future::join_all(tasks).await;

Не надо collect().

Chayim Friedman 06.08.2024 23:32

Другие вопросы по теме