Как сделать структуру с вектором и слайсом

Привет.

Делаю бота условно назовём его Bot1, он хранит некоторый путь path, просто вектор точек, и также текущий участок пути, который логично (наверное) представить как слайс.

struct Bot1<'a> {
    random: RefCell<IsaacRng>,
    path: Vec<Point>,
    cur_view: &'a[Point]
}

Понятно, что я тут же получил кучу ошибок от BC. Очевидный выход использовать индекс начала и конца, но он мне не нравится - придётся вручную отслеживать выходы за пределы path, считать попадание в участок пути и вообще неудобно.
Какие альтернативы ещё есть?

Моя наивная попытка здесь

2 лайка

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

UPD: Потому что самоссылающиеся структуры в расте это боль, по крайней мере пока https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md не приняли. Есть всякие костыльные штуки вроде https://crates.io/crates/rental + можно заморочиться с Rc.
По мне так сильно проще замутить небольшую абстракцию с индексами :slight_smile:

1 лайк

А, раньше казалось что Pin-ы это что-то слишком абстрактное и мне точно не понадобится, ан нет…

На ум приходит что-то вроде структуры данных “вектор с окном”, чтобы можно было менять вектор, и окно соответственно тоже бы менялось.
Попробую такую абстракцию реализовать.
Спасибо.

Ну как, получилось что-то с реализацией этой штуки?

Сделал просто индексом:

#[derive(Clone, Debug)]
pub struct Bot2 {
    ...
    path: Vec<P>,
    path_idx: usize,
    ...
}

Недостаток в том, что теперь в месте использования приходится всегда проверять выход индекса за пределы массива:

       let the_move = if !self.path.is_empty() && self.path_idx < self.path.len() {
            let new_head = self.path[self.path_idx];
            ...
       }

И когда self.path меняется, нужно не забыть пододвинуть self.path_idx нужным образом. И, наконец, если вышли за пределы, то … непонятно что делать. Панику кидать не хочется, как-то надо сигнализировать о нарушении инварианта и восстановить инвариант.
Что-то хочется, чтобы индекс имел тип “индекс в векторе path” и больше гарантий от компилятора.

Т.е. ты не стал путь и индексвы в одну структуру с удобными методами упаковывать? А то можно было бы Result возвращать какой-нибудь и просто ?ом пробрасывать его наверх, если что не так. Или проверять где надо.

Уже сделано в крейте rental, но, судя по количеству загрузок, решение не очень популярное.

Я было начал, но потом меня испугало то, что нужно повторять часть апи для вектора для этой структуры тоже, итераторы всякие и так далее. Как-нибудь потом :slight_smile:

1 лайк

Я совсем недавно в rust, поэтому могу многое не знать, но скажите почему такое решение хуже, чем с отдельными индексами ? Если вы такое рассматривали конечно.

Из того что сразу в голову приходит (может еще что есть), такую структуру нельзя будет нормально переместить или передать по &mut ссылке:

fn f1(_b: &mut Bot1) {}
fn f2(_b: Bot1) {}
...
   
f1(&mut b1);
f2(b1);
error[E0502]: cannot borrow `b1` as mutable because `b1.path` is also borrowed as immutable
  --> src/main.rs:40:13
   |
36 |     b1.cur_view = &b1.path[2..];
   |                    ------- immutable borrow occurs here
...
40 |     f1(&mut b1);
   |             ^^ mutable borrow occurs here
...
44 | }
   | - immutable borrow ends here

error[E0505]: cannot move out of `b1` because it is borrowed
  --> src/main.rs:41:8
   |
36 |     b1.cur_view = &b1.path[2..];
   |                    ------- borrow of `b1.path` occurs here
...
41 |     f2(b1);
   |        ^^ move out of `b1` occurs here

Playground

Хммм точно, сразу как то в голову не пришло )) Спасибо. Как то еще не смог полностью поменять мышление, не улавливаю такие моменты сразу.

я бы делал структуру, с вектором и с индексами текущего начала и конца (Range например).

и эти индексы делал бы приватными.

А публично выставил бы только метод (функцию), вызов которого возвращал бы слайсы с параметрами согласно инфы содержащейся в индесах,.