Shanraq.org Shanraq.org
Срезы: часть данных без копии
IT

Rust: с нуля до своего органайзера Урок 23 из 35

Срезы: часть данных без копии

Урок 23. Передать функции часть числового набора, не создавая новый массив.

Текст переведён с помощью ИИ.

Предварительно: уроки 13, 15 и 20–22. Если границы 1..3 неясны, повторите урок 13.

Образ и опорная карта

Можно показать коллеге две соседние строки в своём списке, не переписывая весь лист. Срез (slice) — представление непрерывной части последовательности без владения её элементами. Образ ограничен: Rust связывает время использования среза с данными и проверяет границы; бумажный лист так себя не защищает.

Владелец → границы → срез для чтения → функция → владелец остаётся. &[u32] означает ссылку на последовательность чисел u32 с известной длиной, но не обещает конкретное число элементов. В отличие от [u32; 4] из урока 15, длина здесь не написана в типе. &minutes[1..3] берёт элементы с индексами 1 и 2: знак .. из урока 13 не включает правую границу. Обе границы должны быть допустимыми.

Считаем середину

В organizer сохраните старый src/main.rs отдельно, затем полностью замените его примером. В папке с Cargo.toml выполните cargo run. Другие файлы и зависимости не меняются. Предскажите результат; ниже нет сообщений Cargo.

До кода разберите новые места. middle ссылается только на два числа исходного массива. values: &[u32] позволяет sum принять такой срез. 0..values.len() создаёт индексы от нуля до длины, не включая длину; поэтому каждый values[index] допустим. u32 копируется как число, но сам массив ради вызова не копируется.

fn sum(values: &[u32]) -> u32 {
    let mut total: u32 = 0;
    for index in 0..values.len() {
        total = total + values[index];
    }
    total
}

fn main() {
    let minutes: [u32; 4] = [10, 20, 15, 5];
    let middle = &minutes[1..3];
    println!("Середина: {}", sum(middle));
    println!("Первое: {}", minutes[0]);
}
Середина: 35
Первое: 10

middle охватывает 20 и 15. Он не может жить дольше minutes. Передача middle не отдаёт владение массивом, поэтому minutes[0] доступен после вызова. Для всего массива можно взять &minutes[0..minutes.len()]; функция не обязана знать длину заранее.

Граница и срок

Если правая граница больше длины, создание среза остановит программу с panic, как выход за границу массива в уроке 15. Для массива из четырёх элементов &minutes[4..4] — пустой допустимый срез, а &minutes[4..5] недопустим. Пустой срез можно передать sum: начальный ноль останется результатом.

Срез — ссылка, поэтому он не может пережить владельца. Следующая отдельная программа намеренно не компилируется с E0597:

fn main() {
    let view: &[u32];
    {
        let values = [10, 20];
        view = &values[0..2];
    }
    println!("{}", view[0]);
}

values заканчивается во внутреннем блоке, а view хотят использовать снаружи. Перенесите владельца во внешний блок или закончите использование среза внутри. Просто изменить имя среза недостаточно.

&str из урока 22 тоже является срезом, но текста. Для текстового среза границы задаются байтами и должны совпадать с границами символов UTF-8. Поэтому не переносите числовые индексы массива прямо на слово; подробности будут в уроке 25.

Проверка понимания

  1. Какие элементы даёт &minutes[1..3]?
  2. Почему sum принимает &[u32], а не [u32; 4]?
  3. Можно ли использовать срез после исчезновения массива?

Проверьте: индексы 1 и 2; функция принимает срезы разных длин без копии массива; нет. Восстановите опорную карту.

Задание

Обязательное. С массивом [5, 10, 15, 20] вызовите свою sum(values: &[u32]) для первых двух и последних двух элементов. Выведите Первые: 15, Последние: 35, затем Всего элементов: 4 из исходного массива. Не создавайте ещё два массива и не вписывайте готовые суммы в вывод.

Граница. Проверьте пустой срез &minutes[0..0]: сумма должна быть нулём. Затем верните основное решение.

Подсказка

Используйте &minutes[0..2] и &minutes[2..4]; оба передайте одной функции. Внутри неё пройдите по 0..values.len().

Эталон после попытки

fn sum(values: &[u32]) -> u32 {
    let mut total: u32 = 0;
    for index in 0..values.len() {
        total = total + values[index];
    }
    total
}

fn main() {
    let minutes: [u32; 4] = [5, 10, 15, 20];
    println!("Первые: {}", sum(&minutes[0..2]));
    println!("Последние: {}", sum(&minutes[2..4]));
    println!("Всего элементов: {}", minutes.len());
}
Первые: 15
Последние: 35
Всего элементов: 4

Одна функция работает с двумя частями массива; владелец остаётся доступен. Официальный разбор срезов.

Предыдущий урок · Оглавление · Следующий урок

Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом

Комментарии (0)

Пока нет комментариев. Будьте первым.