Menu
CoddyTech

Обобщённые функции

Часть раздела Объектно-ориентированное программирование путешествия по Rust на Coddy. Урок 27 из 61.

Обобщения не ограничиваются структурами. Ты также можешь писать отдельные функции, которые работают с любым типом. Это полезно, когда нужна вспомогательная функция, не принадлежащая к конкретной структуре, но при этом остающаяся гибкой.

Синтаксис повторяет то, что вы уже видели в структурах. Объявите обобщённый параметр в угловых скобках после имени функции, а затем используйте его в параметрах и типе возвращаемого значения:

fn identity<T>(value: T) -> T {
    value
}

Эта функция identity принимает значение любого типа и возвращает его без изменений. Запись <T> объявляет обобщённый тип, value: T принимает его в качестве параметра, а -> T задаёт тип возвращаемого значения. При вызове функции Rust выводит конкретный тип:

let num = identity(42);        // T — i32
let text = identity("hello");  // T — &str

В функциях также можно использовать несколько обобщённых параметров, как и в структурах:

fn make_pair<T, U>(first: T, second: U) -> (T, U) {
    (first, second)
}

let pair = make_pair(10, "ten");  // возвращает (i32, &str)

Обобщённые функции позволяют один раз написать повторно используемую логику и применять её к разным типам, сокращая дублирование кода и сохраняя типобезопасность.

challenge icon

Задание

Легко

Давай создадим модуль утилит с обобщёнными функциями, которые могут работать с любым типом! Ты создашь отдельные функции, демонстрирующие, как обобщения делают код гибким и повторно используемым, не привязывая его к определённой структуре.

Ты организуешь код в двух файлах:

  • utils.rs: создай набор общедоступных обобщённых функций-утилит:
    • wrap_in_pair<T>: принимает одно значение и возвращает кортеж, содержащий это значение дважды: (value, value). Для этого тип должен реализовывать Clone, поэтому используй <T: Clone>
    • swap<T, U>: принимает два значения, потенциально разных типов, и возвращает их в обратном порядке в виде кортежа (U, T)
  • main.rs: импортируй свой модуль утилит и продемонстрируй работу этих обобщённых функций с разными типами. Покажи, как одни и те же определения функций без проблем работают с целыми числами, числами с плавающей точкой и строками.

В главном файле продемонстрируй работу функций-утилит, выполнив следующие действия:

  1. Используй wrap_in_pair с целым числом (первый ввод, преобразованный в i32) и выведи оба элемента
  2. Используй wrap_in_pair со строкой (второй ввод) и выведи оба элемента
  3. Используй swap с целым числом (третий ввод, преобразованный в i32) и строкой (четвёртый ввод), затем выведи результат перестановки

Вывод должен соответствовать следующему формату:

Pair of ints: ({value}, {value})
Pair of strings: ({value}, {value})
Swapped: ({string}, {int})

Например, при вводе 5, hello, 42 и world:

Pair of ints: (5, 5)
Pair of strings: (hello, hello)
Swapped: (world, 42)

Обрати внимание, что wrap_in_pair одинаково работает и с целыми числами, и со строками, а swap обрабатывает два совершенно разных типа: в этом и заключается гибкость обобщённых функций!

Ты получишь четыре значения: целое число, строку, ещё одно целое число и ещё одну строку.

Попробуйте сами

mod utils;

use std::io;

fn main() {
    // Считайте четыре входных значения
    let mut input1 = String::new();
    io::stdin().read_line(&mut input1).expect("Failed to read line");
    let num1: i32 = input1.trim().parse().expect("Invalid integer");

    let mut input2 = String::new();
    io::stdin().read_line(&mut input2).expect("Failed to read line");
    let str1 = input2.trim().to_string();

    let mut input3 = String::new();
    io::stdin().read_line(&mut input3).expect("Failed to read line");
    let num2: i32 = input3.trim().parse().expect("Invalid integer");

    let mut input4 = String::new();
    io::stdin().read_line(&mut input4).expect("Failed to read line");
    let str2 = input4.trim().to_string();

    // TODO: Используйте wrap_in_pair с num1 и выведите результат
    // Формат: Pair of ints: ({value}, {value})

    // TODO: Используйте wrap_in_pair с str1 и выведите результат
    // Формат: Pair of strings: ({value}, {value})

    // TODO: Используйте swap с num2 и str2, затем выведите результат обмена
    // Формат: Swapped: ({string}, {int})
}
quiz iconПроверьте себя

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

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

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