Rust: с нуля до своего органайзера Урок 45 из 50
Обобщённый тип: одна функция для разных списков
Урок 45. Прочитать и написать функцию, работающую с разными типами элементов.
Текст переведён с помощью ИИ.
Предварительно: срезы, Option, модули. Сохраните прежний проект и замените src/main.rs примером ниже. Здесь задачи снова живут только в памяти.
Образ и опорная карта
Номерок в гардеробе указывает место: там может висеть пальто или лежать сумка, но правило «найди вещь по номеру» одинаково. Обобщённый тип позволяет описать такое правило один раз. В fn choose<T>(items: &[T], index: usize) -> Option<&T> буква T означает неизвестный пока тип одного элемента. Угловые скобки после имени функции объявляют этот параметр типа; это не сравнение чисел. При вызове с числами компилятор подставляет числовой тип, с задачами — Task. Образ ограничен: функция не умеет делать с T всё подряд; в этом примере она лишь выбирает элемент среза.
Срез &[T] + место usize → get → Some(&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.
Вопросы для воспроизведения
- Что объявляет
<T>послеchoose? - Почему результат —
Option<&T>, а не просто&T? - Равен ли индекс
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)
Войдите, чтобы оставить комментарий →
Пока нет комментариев. Будьте первым.