Несколько обобщённых типов
Часть раздела Объектно-ориентированное программирование путешествия по Rust на Coddy. Урок 26 из 61.
Иногда одного generic-параметра недостаточно. Что, если тебе нужен struct, содержащий два значения, причём эти значения могут иметь разные типы? Rust позволяет использовать несколько generic-параметров, разделяя их запятыми.
Вот структура Pair, которая содержит два значения, потенциально имеющих разные типы:
struct Pair<T, U> {
first: T,
second: U,
}
Конструкция <T, U> объявляет два независимых параметра типа. T и U могут быть одним и тем же типом или совершенно разными типами — это зависит от тебя при создании экземпляра:
let both_ints = Pair { first: 10, second: 20 }; // T=i32, U=i32
let mixed = Pair { first: "hello", second: 3.14 }; // T=&str, U=f64
let reversed = Pair { first: 100, second: "world" }; // T=i32, U=&str
При реализации methods объявите все generic-параметры в блоке impl:
impl<T, U> Pair<T, U> {
fn get_first(&self) -> &T {
&self.first
}
fn get_second(&self) -> &U {
&self.second
}
}
Этот шаблон часто используется, когда нужно связать два взаимосвязанных, но потенциально разных фрагмента данных, например пару key-value, координаты с разными числовыми типами или результат, дополненный метаданными.
Задание
ЛегкоДавайте создадим систему хранения пар ключ–значение, которая продемонстрирует возможности нескольких generic типов! Вы создадите структуру KeyValue, способную объединять любые два типа, что идеально подходит для связывания меток с данными, идентификаторов с объектами или любых других комбинаций.
Вы организуете код в двух файлах:
keyvalue.rs: Define публичную generic структуру с именемKeyValue<K, V>с двумя private полями:keyтипаKиvalueтипаV. Implement methods для работы с этой структурой:- associated function
new, которая createsKeyValueиз key и value - method
key, которая returns reference на key - method
value, которая returns reference на value - method
set_value, которая replaces current value новым
- associated function
main.rs: Подключите свой модуль и продемонстрируйте, как одно и то же определение структуры работает с различными комбинациями типов. Вы создадите пары key-value со mixed типами, чтобы показать гибкость нескольких generic типов.
В основном файле создайте и используйте следующие пары key-value:
- Пару со строковым key и целочисленным value (first input в качестве key, second input, разобранный как
i32, в качестве value) - Выведите key и value
- Обновите value до нового целого числа (third input, разобранный как
i32) - Выведите обновлённый value
- Создайте second пару с целочисленным key и строковым value (fourth input, разобранный как
i32, в качестве key, fifth input в качестве value) - Выведите key и value этой пары
Ваш вывод должен соответствовать этому формату:
Key: {key}, Value: {value}
Updated value: {value}
Key: {key}, Value: {value}Например, для inputs name, 42, 100, 7 и lucky:
Key: name, Value: 42
Updated value: 100
Key: 7, Value: luckyОбратите внимание: первая пара использует KeyValue<String, i32>, тогда как вторая использует KeyValue<i32, String>: одна и та же структура, разные комбинации типов!
Вы получите five inputs: строковый key, целочисленный value, replacement integer, целочисленный key и строковый value.
Попробуйте сами
mod keyvalue;
use keyvalue::KeyValue;
fn main() {
// Прочитать пять входных данных
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let string_key = input1.trim().to_string();
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let int_value: i32 = input2.trim().parse().expect("Failed to parse");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let replacement_int: i32 = input3.trim().parse().expect("Failed to parse");
let mut input4 = String::new();
std::io::stdin().read_line(&mut input4).expect("Failed to read line");
let int_key: i32 = input4.trim().parse().expect("Failed to parse");
let mut input5 = String::new();
std::io::stdin().read_line(&mut input5).expect("Failed to read line");
let string_value = input5.trim().to_string();
// TODO: Создать пару KeyValue<String, i32> используя string_key и int_value
// TODO: Вывести ключ и значение в формате: "Key: {key}, Value: {value}"
// TODO: Обновить значение используя set_value с replacement_int
// TODO: Print the updated value in format: "Updated value: {value}"
// TODO: Создать пару KeyValue<i32, String> используя int_key и string_value
// TODO: Вывести ключ и значение этой пары в формате: "Key: {key}, Value: {value}"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media PlayerПотренируйтесь самостоятельно: Онлайн-компилятор Rust