Shanraq.org Shanraq.org
Обобщённый тип: одна функция для разных списков
IT

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

Обобщённый тип: одна функция для разных списков

Урок 45. Прочитать и написать функцию, работающую с разными типами элементов.

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

Предварительно: срезы, Option, модули. Сохраните прежний проект и замените src/main.rs примером ниже. Здесь задачи снова живут только в памяти.

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

Номерок в гардеробе указывает место: там может висеть пальто или лежать сумка, но правило «найди вещь по номеру» одинаково. Обобщённый тип позволяет описать такое правило один раз. В fn choose<T>(items: &[T], index: usize) -> Option<&T> буква T означает неизвестный пока тип одного элемента. Угловые скобки после имени функции объявляют этот параметр типа; это не сравнение чисел. При вызове с числами компилятор подставляет числовой тип, с задачами — Task. Образ ограничен: функция не умеет делать с T всё подряд; в этом примере она лишь выбирает элемент среза.

Срез &[T] + место usizegetSome(&T) или None. &[T] — заимствованный срез элементов одного типа, usize — беззнаковый целый тип для индексов; его ширина зависит от платформы. get безопасно проверяет границу; неизвестное место возвращает None, а не останавливает программу. Option<&T> возвращает ссылку, поэтому функция не забирает элемент из массива. В -> &T символ & значит ссылка; здесь Option добавляет случай отсутствия. Явное обозначение времени жизни ссылки не нужно: единственный заимствованный вход items определяет, с чем связан выход. Полные правила этой связи будут в уроке о временах жизни. Знак <> уже встречался у Vec<T>; теперь мы объявляем свой T, а не только читаем готовый тип.

Запустите один алгоритм с двумя типами

Замените src/main.rs и выполните cargo run. Новых зависимостей нет. Cargo может печатать служебные строки; ниже только результат программы.

fn choose<T>(items: &[T], index: usize) -> Option<&T> {
    items.get(index)
}

struct Task {
    title: String,
}

fn main() {
    let numbers = [10, 20];
    let tasks = [Task {
        title: String::from("Читать Rust"),
    }];
    if let Some(number) = choose(&numbers, 1) {
        println!("Число: {number}");
    }
    if let Some(task) = choose(&tasks, 0) {
        println!("Задача: {}", task.title);
    }
    println!("Нет элемента: {}", choose(&numbers, 9).is_none());
}
Число: 20
Задача: Читать Rust
Нет элемента: true

choose(&numbers, 9) возвращает None; is_none() даёт true. Если бы мы написали items[index], неверный индекс привёл бы к панике. Для реального поиска задачи по устойчивому ID этот приём сам по себе не подходит: индекс в срезе меняется при удалении, как показал урок 38.

Вопросы для воспроизведения

  1. Что объявляет <T> после choose?
  2. Почему результат — Option<&T>, а не просто &T?
  3. Равен ли индекс 0 устойчивому ID задачи?

Задание

Обязательное. Напишите собственную fn last<T>(items: &[T]) -> Option<&T>, которая возвращает последний элемент или None для пустого среза. Примените её к tasks, напечатайте Последняя: Читать Rust, затем проверьте пустой массив чисел. Не используйте индекс len() - 1: при пустом массиве это ошибка.

Ответы

Показать ответы

У среза есть метод last(), который уже возвращает Option<&T>. Вызовите его внутри своей функции и обработайте Some в main.

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

fn choose<T>(items: &[T], index: usize) -> Option<&T> {
    items.get(index)
}

fn last<T>(items: &[T]) -> Option<&T> {
    items.last()
}

struct Task {
    title: String,
}

fn main() {
    let numbers = [10, 20];
    let tasks = [Task {
        title: String::from("Читать Rust"),
    }];
    if let Some(number) = choose(&numbers, 1) {
        println!("Число: {number}");
    }
    if let Some(task) = choose(&tasks, 0) {
        println!("Задача: {}", task.title);
    }
    println!("Нет элемента: {}", choose(&numbers, 9).is_none());
    if let Some(task) = last(&tasks) {
        println!("Последняя: {}", task.title);
    }
    let empty: [u32; 0] = [];
    println!("Пустой список: {}", last(&empty).is_none());
}
Число: 20
Задача: Читать Rust
Нет элемента: true
Последняя: Читать Rust
Пустой список: true

После проверки

Для пустого массива last(&[]) вернёт None; тип пустого массива нужно указать, например let empty: [u32; 0] = [];. Официальная глава об обобщённых типах. Если Some и None неясны, повторите урок 31.

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

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

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

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