Shanraq.org Shanraq.org
Массивы
IT

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

Массивы

Урок 15. Обработать фиксированный набор длительностей и проверить границы.

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

Предварительно: уроки 1–14. Если непонятны переменные, вернитесь к уроку 9.

Зачем это нужно

До этого отдельной задаче соответствовала отдельная переменная. Массив — упорядоченная последовательность значений одного типа с длиной, известной при компиляции. Значения могут повторяться. Представьте лоток с тремя подписанными отделениями. В нём можно заменить содержимое, но нельзя добавить четвёртое отделение к тому же массиву. Все элементы должны иметь один тип; похожие по смыслу данные разных типов не подходят автоматически.

Сохраним длительность трёх задач и посчитаем общую. Повторение возьмём из урока 13. Это пока числовая сводка, а не полная запись задачи с названием и состоянием: такие записи появятся со структурами.

Запуск примера

Работаем в проекте organizer, созданном в уроке 6, в папке с Cargo.toml. Сохраните свою предыдущую работу отдельно. Замените весь src/main.rs первым примером ниже, сохраните файл и выполните cargo run в терминале этой папки. Как объяснялось в уроке 6, команда собирает и запускает программу. Остальные файлы менять не нужно; зависимости не добавляем. Каждый следующий полный пример также заменяет файл целиком. В блоке результата показан только вывод программы, без сообщений Cargo. Сначала предскажите его, потом запускайте.

Перед первым примером разберём скобки. В типе [u32; 3] квадратные скобки описывают массив: три элемента типа u32; ; внутри отделяет тип от длины. В значении [10, 20, 15] запятые разделяют элементы. minutes[0] берёт первый элемент, поскольку счёт позиций начинается с нуля. minutes.len() вызывает метод len через точку и возвращает число элементов. Представьте лоток с тремя ячейками: ячейки пронумерованы 0, 1, 2, а их количество — 3.

fn main() {
    let minutes: [u32; 3] = [10, 20, 15];
    println!("Первая: {}", minutes[0]);
    println!("Количество: {}", minutes.len());
    let mut total: u32 = 0;
    for value in minutes {
        total = total + value;
    }
    println!("Всего минут: {total}");
}
Первая: 10
Количество: 3
Всего минут: 45

Разбор

В типе [u32; 3] u32 обозначает тип элемента, а 3 — число элементов. Квадратные скобки окружают описание массива; точка с запятой внутри отделяет тип от длины, а не завершает инструкцию. [10, 20, 15] создаёт значение массива: числа разделяются запятыми. Длина входит в тип, поэтому [u32; 3] и [u32; 4] — разные типы.

Индекс — номер позиции, начиная с нуля. minutes[0] — первый элемент; второй — minutes[1], третий — minutes[2]. Не смешивайте порядковый номер в человеческом описании и индекс в коде. У массива длины 3 нет элемента с индексом 3.

minutes.len() сообщает длину. Здесь впервые встречается вызов метода: точка связывает действие len со значением minutes, круглые скобки вызывают его без дополнительных аргументов. Своих методов сейчас не пишем; подробнее они будут в уроке 28. Метод len возвращает usize — беззнаковый целый тип для размеров и индексов, зависящий от платформы. Например, let index: usize = 1; задаёт индекс явно. Это не то же самое, что число минут типа u32; не пытайтесь складывать их без обоснования.

for value in minutes берёт значения массива по порядку. value — очередное число, не индекс. Поэтому здесь удобнее обходить значения напрямую, без 0..minutes.len(). В этом примере числа u32 и массив из них копируются как значения; правил владения для строк это не объясняет — их разберём в уроках 18–22.

Изменение и проверка границ

let mut позволяет менять элементы, но не длину массива. Следующая отдельная программа заменяет второй элемент и проверяет запрошенный индекс до обращения:

fn print_at(minutes: [u32; 3], index: usize) {
    if index < minutes.len() {
        println!("{}", minutes[index]);
    } else {
        println!("Нет такой позиции");
    }
}

fn main() {
    let mut minutes: [u32; 3] = [10, 20, 15];
    minutes[1] = 25;
    print_at(minutes, 3);
}
Нет такой позиции

print_at — обычная функция из приёмов урока 14. Её первый параметр — весь массив [u32; 3], второй — индекс usize. Вызов print_at(minutes, 3) передаёт их по порядку; функция возвращает () и сама проверяет индекс. Это позволит позже проверять индекс, пришедший извне, а пока аргумент задан в коде.

При index = 3 доступ не выполняется. Поменяйте индекс на 1: получите 25. Условие строгое: index < minutes.len(), не <=. Для пустого массива нет допустимого индекса вообще. Нельзя заранее вычислить minutes[index], а потом проверить границы — будет поздно.

Доступ за границей массива вызывает панику (panic): программа прекращает обычное выполнение с сообщением об ошибке. Когда неверный индекс очевиден ещё при компиляции, компилятор может отклонить код заранее. Rust не разрешает просто прочитать соседнюю память. Но аварийная остановка всё равно мешает пользователю, поэтому сначала проверяем индекс. Более удобный способ получать элемент без риска выхода за границу изучим в уроке 31.

Есть сокращённая запись одинаковых элементов: let minutes: [u32; 3] = [0; 3]; создаёт три нуля. В значении [0; 3] слева начальное значение, справа количество. В типе [u32; 3] слева тип. Это два разных применения похожей записи.

Типичная ошибка

Обещаны три элемента, а записаны два. Этот пример намеренно не компилируется: E0308.

fn main() {
    let minutes: [u32; 3] = [10, 20];
    println!("{}", minutes.len());
}

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

Опорная карта

Один тип и длина → значения → обход → отбор функцией → накопление результата.

Проверьте понимание

  1. Какой последний индекс у массива длины 4?
  2. Меняет ли mut длину?
  3. Чем значение в for value in minutes отличается от индекса?

Сверка: 3; нет; value — длительность, а индекс — позиция. Готовы к следующей партии, если можете объяснить отбор, цикл и вызов функции в своём решении. В 16-м уроке проверим вычисления автоматическими тестами.

Задание

Обязательное. Возьмите [0, 10, 25, 15] типа [u32; 4]. Используйте is_short из урока 14: короткой считается длительность 1–15 минут включительно. Через for посчитайте количество коротких задач и их суммарную длительность. Ожидается «Коротких задач: 2» и «Всего минут: 25».

Проверка своими данными. Для [0, 16, 25, 30] получите 0 и 0, для [1, 15, 2, 3] — 4 и 21. Ноль не считается задачей длительностью 0 минут: таково наше правило. Меняйте только данные, не формулы.

Подсказка

Объявите два накопителя перед циклом. Внутри if is_short(value) увеличьте количество на 1, а сумму — на value. Функция из предыдущего урока остаётся вне main.

Эталон после самостоятельной попытки

fn is_short(minutes: u32) -> bool {
    minutes > 0 && minutes <= 15
}

fn main() {
    let minutes: [u32; 4] = [0, 10, 25, 15];
    let mut count: u32 = 0;
    let mut total: u32 = 0;
    for value in minutes {
        if is_short(value) {
            count = count + 1;
            total = total + value;
        }
    }
    println!("Коротких задач: {count}");
    println!("Всего минут: {total}");
}
Коротких задач: 2
Всего минут: 25

Источник для проверки

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

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

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

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