Соседние клетки



Я редко пишу что-то в этот канал, потому что на интересные посты обычно нужно потратить много времени. Но само его наличие мне очень нравится! Например, часто в комментарии приходят умные люди и рассказывают, как на самом деле что-то надо было сделать :)



Так вот, представим, что у нас есть клеточное поле размером width*height и вы пишете bfs на нем. Есть текущая клетка (row, col). Как обойти всех непосещенных соседей (по стороне) этой клетки?



В совсем крайнем случае можно написать 4 похожих куска кода:

if row + 1 < height && !used[row + 1][col] {

used[row + 1][col] = true;

queue.push((row + 1, col));

}

if col + 1 < width && !used[row][col + 1] {

...

}




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

for (dr, dc) in [(0, 1), (1, 0), (0, -1), (-1, 0)] {

let nrow = row + dr;

let ncol = col + dc;

if 0 <= nrow && nrow < height && 0 <= ncol && ncol < width && !used[nrow][ncol] {

used[nrow][ncol] = true;

queue.push((nrow, ncol));

}

}




Это бы нормально работало, если бы все переменные были типа i32. Но в Rust массивы индексируются только типом usize, который беззнаковый. Поэтому в каком-то месте придется приводить типы. Условно:

used[nrow as usize][ncol as usize]


И это выглядит некрасиво. Плюс придется из-за этого сделать width и height типа i32, а интуитивно кажется, что они должны быть usize, иначе придется много где добавлять лишние касты.



Так вот, как проще всего писать такой код на Rust?



P. S. Я еще видел такой вариант (overflowing_add и .0 это обычное сложение, которое разрешает переполнение):

for (dr, dc) in [(0, 1), (1, 0), (0, !0), (!0, 0)] {

let nrow = row.overflowing_add(dr).0;

let ncol = col.overflowing_add(dc).0;

if nrow < height && ncol < width && !used[nrow][ncol] {

used[nrow][ncol] = true;

queue.push((nrow, ncol));

}

}