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

Тест требует, чтобы я принял grid: Vec<Vec<char>>, но примеры представлены в виде массивов:

let grid  = [
    ["1","1","0","0","0"],
    ["1","1","0","0","0"],
    ["0","0","1","0","0"],
    ["0","0","0","1","1"]
];

Который имеет тип [[&str;5];4]. Руками делать конвертацию точно не хочется, поэтому нужно написать функцию из [[&str;T];U] в Vec<Vec<char>>.

Я случайно написал [&str;U], но является ли это разумным способом определения универсального? Я добрался до ощущения шаткости вокруг среза, поэтому остановлюсь на этой проблеме.

…и, бумер. Типы массивов Core Rust [T; N] не могут использоваться в общем случае по отношению к N

Грустный. Но нужно ли мне это? Можем ли мы жить с &[]?

Давайте попробуем написать простую функцию, которая принимает любой массив и возвращает его длину.

Да, мы вполне можем.

fn foo(arr: &[u8]) -> usize {
  arr.len()
}

Можем ли мы взять &[&[...]] и вернуть длину внешнего массива?

fn foo(arr: &[&[u8]]) -> usize {
  arr.len()
}

и тест:

#[test]
fn test_1(){
  let data = [&[1,2], &[3,4]];
  assert_eq!(foo(&data), 3);
}

Ошибка была немного запутанной:

Что? Это действительно неожиданно, так как я предположил, что Rust может сделать вывод, что данные равны [&[u8]] вместо {integer}. Что происходит?

Если я закомментирую «assert», Rust может показать мне предполагаемый тип данных:

&[&[i32; 2]; 2]

который не является &[&[u8]] (игнорировать u8), который не является &[&[u32]].

Давайте отступим.

let data = &[1,2];  // type &[i32; 2]

Но мы можем передать его как аргумент с сигнатурой &[i32].

Существует интересное преобразование между ссылкой на массив и ссылкой на срез. Я могу сильно ошибаться, но похоже на .as_ref() звонок.

я могу написать

let z: &[i32] = data.as_ref()
// or just
let z: &[i32] = data; // implicit call to .as_ref()?

Но неявного вызова .as_ref() для вложенных ссылок нет… Могу ли я сделать это явно?

let data = [
  &[1,2].as_ref(),
  &[3,4].as_ref()
].as_ref();

Результирующий тип &[&&[i32]]. Не совсем… но…

let data = [
  [1,2].as_ref(),
  [3,4].as_ref()
].as_ref();

Да, теперь у нас есть тип, который мы хотим: &[&&[i32]].

Но почему первая версия не работала? О, это приоритет…

let data = (&[
    (&[1,2]).as_ref(),
    (&[3,4]).as_ref()
]).as_ref();

Эта версия тоже работала, но с большим количеством нажатых кнопок. Я думаю, что у меня проблема, поэтому давайте вернемся к исходной проблеме:

опа, нет. Ржавчина наносит ответный удар:

По сути, я создаю массив, который удаляется в конце «]», поэтому ссылки становятся недействительными. Хм. Я не могу просто использовать «let» для каждой строки. Могу ли я использовать исходный массив массивов для построения массива заимствований?

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

let data = [
  [1,2],
  [3,4],
];
let refs = data.map(|x| x.as_ref()).as_ref();

Нет, Rust находится в режиме полного укуса:

Похоже на тупик.

Я нашел вот это: https://practice.rs/generics-traits/const-generics.html

Бинго! Бинго! Бинго!

fn foo<const N:usize, const M:usize>(arr: [[u8;M];N]) -> usize {
  arr.len()
}
#[test]
fn test_1(){
  let data = [
    [1,2],
    [3,4],
  ];
  assert_eq!(foo(data), 2);
}

Это сработало, и это сработало так удивительно, как я и не мечтал.

Я немного читал о константных дженериках в последних примечаниях к выпуску (не в последних… время летит), но так и не смог их использовать. Это первый раз, и они меня очень спасли.

Реализация массива -› Vec‹Vec‹››

Теперь я могу написать свою функцию преобразования.

fn convert<
  T: std::clone::Clone,
  const N: usize,
  const M: usize
>(arr: [[T; M]; N]) -> Vec<Vec<T>> {
  let mut ret = Vec::with_capacity(N);
  for v in arr.iter() {
    ret.push(v.to_vec());
  }
  ret
}

Rust пришел на помощь и сказал, что мне нужно T: std::clone::Clone, но, похоже, это сработало!

(на самом деле мне нужно было еще и &str[0] преобразовать в char, но это мелочь).

Заключение

В Rust есть константные дженерики, и они позволяют реализовать дженерики для массивов произвольной длины.

P.S.

Фактическая функция преобразования:

fn convert<const N: usize, const M: usize>
(arr: [[&str; M]; N]) -> Vec<Vec<char>> {
  let mut ret = Vec::with_capacity(N);
  for row in arr.iter(){
    let mut vec_row = Vec::with_capacity(M);
    for cell in (*row).iter(){
      let char = (*cell).chars().next().unwrap();
      vec_row.push(char);
    }
    ret.push(vec_row)
  }
  ret
}

…и у меня получилось «на 100% быстрее» с первой попытки!