LINUX.ORG.RU

Массивы в Rust

 


0

5

Продолжаю изучать Rust и вот решил попробовать в нём массивы.

Массивы в Rust неитерабельные, тоесть нельзя написать for item in array {...}. Вместо этого придётся написать либо for item in &array {...} либо for item in array.iter() {...} либо for item in array.to_vec() {...} либо что-то другое похожее, что создаёт из массива объект итерабельного типа. Ну или использовать цикл, который бежит по индексам for i in 0..array.len() { let n = array[i]; ...}. По моему это недостаток Rust. Для примера в Java for-each неявно разворачивается в одну из двух версий: с использованием индекса или с использованием итератора - в зависимости от того массив ли это или Iterable. Rust умеет делать лишь второй вариант, хотя это такой же синтаксический сахар, как и for-each в Java.

Так же, на сколько я понял, массив в Rust является не объектом, как в Java, а просто куском памяти с обвесами из макросов, как в C/C++. Таким образом функция не может получить, в качестве параметра, массив, длина которого неизвестна во время компиляции. Следующие три варианта функции не скомпилируются с тремя разными сообщениями об ошибке:

fn print_array1(array: [i32]) {
    //
}

fn print_array2(array: [i32; n], n: i32) {
    //
}

fn print_array3(n: i32, array: [i32; n]) {
    //
}

Варианты вроде fn print_array(array: [i32; 27]) {...} работать будут, но практической пользы не имеют. Значит придётся передавать не массив, а созданный из него итерабельный объект. Но это приводит к накладным расходам и с точки зрения системного программирования выглядит не очень хорошо. Может быть это ограничение снято в unsafe?

Ну хорошо, пусть речь идёт не о системном, а о прикладном программировании. Предположим, что я хочу пройтись по всем элементам двумерного массива, например, чтобы распечатать его в виде таблицы. В этом случае удобнее сразу пользоваться не массивом, а скажем слайсом (срезом), более похожим на массив в Java. В итоге у меня получился следующий код:
fn main() {
    let desk: &[&[i32]] = &[&[1, 2, 3, 4], &[5, 6, 7, 8], &[9, 10, 11, 12], &[13, 14, 15, 16]];

    print_desk(desk)
}

fn print_desk(desk: &[&[i32]]) {
    for line in desk {
        for cell in *line {
            print!("{:02} ", cell)
        }
        println!()
    }
}

Здесь у меня возникла проблема во внутреннем цикле. Я полагал, что типом переменной line является &[i32], но оказалост, что он &&[i32]. Поэтому во внутреннем цикле пришлось бежать не по line, а по *line. Другими вариантами были line.iter() или line.to_vec(). Почему здесь такой неконсистентный синтаксис? Так же, почему в сообщении об ошибке предлагается использовать более накладный line.iter() а не *line?

★★★★★
Ответ на: комментарий от bbk123

https://godbolt.org/g/9ovvou

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

Первые две совершенно одинаковые, и никих «обьектов» в твоем понимании не создается.

А вот третьей функции приходится копировать весь массив.

pftBest ★★★★
()
Последнее исправление: pftBest (всего исправлений: 1)
Ответ на: комментарий от bbk123

Посмотри на сигнатуры и сразу станет всё очевидно.

Разница в том как передается владение:

  • 1. Vec можно проитерировать без передачи владения в итератор: по этому можно сделать заимствущий итератор iter.
  • 2. Не все структуры нельзя проитерировать без поглащения итерируемой структуры, для этого есть into_iter, семантика префикса into_ описана в документации.
  • 3. Соответственно into_iter может быть использован вместо iter, покуда ограничение владения выше у поглащающего владение метода, но семантика у такого взятия итератора иная.
mersinvald ★★★★★
()
Последнее исправление: mersinvald (всего исправлений: 1)

Никто ещё не упомянул макросы, так что оставлю это здесь.

macro_rules! print_matrix {
    ($matrix:expr) => {{
        for row in $matrix.iter() {
            for cell in row.iter() {
                println!("{:02}", cell);
            }
        }
    }}
}

fn main() {
    let matrix = [
        [1, 2, 3, 4],
        [5, 6, 7, 8],
        [9, 10, 11, 12],
        [13, 14, 15, 16],
    ];
    print_matrix!(matrix);
}
Ждём-с пока const generics решат эту проблему.

Deleted
()
Ответ на: комментарий от pftBest

Спасибо за иллюстрацию и полезный сайт.

bbk123 ★★★★★
() автор топика
Ответ на: комментарий от pftBest

https://godbolt.org/g/4gNR6u

Добавил ещё iter()

example::call_4:
  lea rdi, [rip + .Lref.0]
  lea rsi, [rip + .Lref.0+28]
  jmp foo4@PLT

и into_iter()

example::call_5:
  lea rdi, [rip + .Lref.0]
  lea rsi, [rip + .Lref.0+28]
  jmp foo4@PLT
red75prim ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.