LINUX.ORG.RU

Вопрос по rust

 


0

3

Доброго времени суток,

Код-сниппет : https://pastebin.com/28JGG03C .

Проблема : Есть три структуры Databatch, Dataloader, Reactor. Реактор содержит загрузчик данных, а загрузчик данных возвращает один из &Databatch.

На 44 строчке ошибка :

cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:44:9 

Вопрос : Как мне переделать архитектуру или часть кода, чтобы реализовать задуманное?

Мне помогло :

  1. Сделать что бы метод next() возвращал Databatch, а не &Databatch
  2. И соответственно вызов .clone() на каждый next()

Но мне не нравится это решение так как происходит лишнее копирование.

Как-то так?

use std::vec::Vec;

// simple batch
struct Batch {
    pub x: i32,
    pub y: i32,
}

// dataloader with counter
struct Dataloader {
    pub v: Vec<Batch>,
}

impl Dataloader {
    fn new() -> Self {
        Self { v: Vec::new() }
    }

    fn next(&mut self) -> Option<Batch> {
        self.v.pop()
    }
}

struct Reactor {
    pub data: Dataloader,
    pub some_counter: i32,
}

impl Reactor {
    fn step(&mut self) {
        if let Some(loaded_data) = self.data.next() {
            self.handle_batch(&loaded_data);
        }
    }

    fn handle_batch(&mut self, batch: &Batch) {
        println!("x : {} , y : {}", batch.x, batch.y);
        self.some_counter += 1;
    }
}

fn main() {
    let mut dl = Dataloader::new();

    // initialize dataloader
    dl.v.push(Batch { x: 10, y: 15 });
    dl.v.push(Batch { x: 8, y: 20 });

    let mut reactor = Reactor {
        data: dl,
        some_counter: 0,
    };
    reactor.step();
}

anonymous-angler ★☆
()

Насколько я понял вся проблема в том, что ты хранишь ссылку на часть reactor’а и при этом передаёшь его же в другую функцию, не освобождая ссылку. С этим бороучекер не может справиться.

Соответственно если хочешь без копирований, то нужно либо держать dataloader отдельным объектом, чтобы ссылка на него не лочила reactor, либо вызывать всё в одной функции (просто переносишь вызов self.data.next() в handle_batch() и всё будет ок), ну или копировать, что освобождает self.

Ну и как отметили выше next() будет паниковать при пустом массиве, или выдавать мусор.

Ivan_qrt ★★★★★
()

Проблема в том, что борроу-чекер не знает что handle_batch не меняет self.data а меняет только self.some_counter поэтому полагает что весь реактор заимствован эксклюзивно (borrowed as mutable).

Простое объединение функций step и handle_batch в одну убирaeт ошибку компиляции.

Полагаю, это не совсем реальный код, а упрощённый и объединение методов нежелательно. Тогда несколько вариантов:

  1. если Batch это маленький объект, то можно сделать для него #[derive(Clone, Copy)] и передавать по-значению.
  2. если Batch сложный, то можно обернуть его в Rc или Arc.
  3. отделить обработку от реактора
struct Reactor {
    pub data: Dataloader,
    pub some_counter: i32,
    pub handler: Handler,
}

impl Reactor {
    fn step(&mut self) {
        let loaded_data = self.data.next();
        self.handler.handle_batch(loaded_data);
        self.some_counter += 1;
    }
}

struct Handler;

impl Handler {
    fn handle_batch(&self, batch: &Batch) {
        println!("x : {} , y : {}", batch.x, batch.y);
    }
}

k_andy ★★★
()

Код-сниппет : https://pastebin.com/28JGG03C .

Хочу посоветовать замечательный сервис https://play.rust-lang.org/, потому что выкладывать сниппеты на Rust где-то, где их нельзя компильнуть, как-то мемно в 2022 году.

Virtuos86 ★★★★★
()
Ответ на: комментарий от anonymous-angler

В вашем коде - вы удаляете данные из вектора. В моём же случае нужно идти от 0..n и заново, не удаляя данные.

xionovermazes
() автор топика
Ответ на: комментарий от anonymous-angler

насчёт self.c == 0 - вы правы. код упрощен, поэтому не заметил.

xionovermazes
() автор топика
Последнее исправление: xionovermazes (всего исправлений: 1)
Ответ на: комментарий от xionovermazes

Тогда пункт 1 не имеет смысла. Получить Databatch вместо &Databatch без копирования или изъястия элемента - невозможно.

anonymous-angler ★☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.