Menu
CoddyTech

Несколько обобщённых типов

Часть раздела Объектно-ориентированное программирование путешествия по 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, координаты с разными числовыми типами или результат, дополненный метаданными.

challenge icon

Задание

Легко

Давайте создадим систему хранения пар ключ–значение, которая продемонстрирует возможности нескольких generic типов! Вы создадите структуру KeyValue, способную объединять любые два типа, что идеально подходит для связывания меток с данными, идентификаторов с объектами или любых других комбинаций.

Вы организуете код в двух файлах:

  • keyvalue.rs: Define публичную generic структуру с именем KeyValue<K, V> с двумя private полями: key типа K и value типа V. Implement methods для работы с этой структурой:
    • associated function new, которая creates KeyValue из key и value
    • method key, которая returns reference на key
    • method value, которая returns reference на value
    • method set_value, которая replaces current value новым
  • main.rs: Подключите свой модуль и продемонстрируйте, как одно и то же определение структуры работает с различными комбинациями типов. Вы создадите пары key-value со mixed типами, чтобы показать гибкость нескольких generic типов.

В основном файле создайте и используйте следующие пары key-value:

  1. Пару со строковым key и целочисленным value (first input в качестве key, second input, разобранный как i32, в качестве value)
  2. Выведите key и value
  3. Обновите value до нового целого числа (third input, разобранный как i32)
  4. Выведите обновлённый value
  5. Создайте second пару с целочисленным key и строковым value (fourth input, разобранный как i32, в качестве key, fifth input в качестве value)
  6. Выведите 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}"
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Rust