"Согрешил" ли я?


#1

В коде необходимо обеспечить уникальность курсора ’ & mut Cursor’ он бережно передается от одного объекта некоторого состояния, другому.

Одним из таких объектов является структура Items которая в себе содержит ссылку на Cursor и массив с дополнительными параметрами описывающими координаты курсора.

struct Items<'a> { cur: &'a mut Cursor, dims: [usize; 3] }

проблема в том, что далее в коде, курсор перезанимается в последующий объект item

while let Some(item) = items.next()
{
     //cannot access items at this scope
}

Но(!) в процессе цикла мне необходим доступ к постоянно меняющимся координатам dims

был вариант когда items.next() возвращал tuple c копией координат плюс item… но копировать, на каждой итерации не умно

соответственно искал выход. нашел такой

impl Items<'_> {
         fn dims(&self) -> &'static [usize; 3] { unsafe { transmute(&self.dims) } }
}

получаю ссылку на массив, содержимое которого во время итерации постоянно обновляется

let dims = items.dims();// getting additional params array

while let Some(item) = items.next()
{
     let d0 = dims[0]; // no problem!
}

при этом задача обеспечения уникальность курсора ’ & mut Cursor’ тоже выполняется на отличненько.

какие подводные камни я не вижу?


#2

А что если избежать все подводные камни, храня не &'a mut Cursor, а &'a RefCell<Cursor>?


#3

RefCell<Cursor> ?

не могу сообразить что это даст


#4

Вроде в чате уже ответили, но это правда UB и компилятор имеет право что угодно тебе поломать оптимизациями из-за этого. Если оно работает сейчас, то совершенно спокойно и без предупреждений может сломаться в следующих версиях компилятора или при сборке под другие ОС. Плюсую мнение что тут надо просто скопировать эти три usize’а, это очень дешевая операция, которую компилятору еще и соптимизировать просто.


UPD: И да, доклад отличный кинули


#5

спасибо. есть о чём подумать.
покопаю в сторону RefCell<Cursor>

================================

покопал.
нет, RefCell тут не применима. почему?

потому, что меняется
и курсор
и дополнительная информация вокруг него (массив dims).

но.

  • всё происходит в одном потоке
  • все изменения происходят только в методах структуры Items
  • коду с снаружи нужен доступ только к дополнительной информации и только на чтение

#6

RefCell “спрячет” мутабельность Cursor за собой и можно будет спокойно делать:

fn dims(&self) -> &'_ [usize; 3] {
 &self.dims
}

а если нужно будет его изменить, то то-же пожалуйста:

fn update_cursor(&mut self, data: ()) {
 *self.cursor.borrow_mut() = data;
 self.dims = [1, 2, 3];
}

на всякий случай привожу сигнатуру Items:

struct Items<'a> {
 cursor: &'a RefCell<Cursor>, 
 dims: [usize; 3],
}

#7

большое спасибо
но пошел по другому пути
разломил Items на две части совсем выкусил массив dims
пользователь будет вынужден самостоятельно создать его, поскольку метод next() будет этого требовать

получилось даже лучше чем было

struct Items<'a>  (&'a mut Cursor);

impl Items<'_> {
	fn next(&mut self, dims: &mut [usize; 3]) -> Option<Option<Values>> {
		....
	}

fn main() {
.....

let mut dims = [0usize;3]; // additional params array 

	while let Some(item) = items.next(& mut dims)
		{
              .............
                        let d0 = dims[0]; // no problem!
        }
}

#8

Андрей,

мой код несколько иначе работает.
в методе next происходит мутация и курсора и дополнительной информации одновременно. так вот, трабл в том, что в этот момент Items залочен ибо &mut self и он передан в возвращенный экземпляр Values

соответственно к dims тоже доступа нет.
да и панику словить в рантайме тоже… так себе удовольствие

если будет интересно разобраться могу выложить код полностью.

есть какая то магия в Rust, заставляющая искать более лучшие решения.


#9

Я наверное не до конца понимаю логику приложения.
Если вы нашли решение и оно вам нравится, так и оставите его