Я добрался до следующего кроссворда 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% быстрее» с первой попытки!