Menu
Coddy logo textTech

Обобщённые методы

Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 25 из 61.

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

Ключевым моментом является объявление универсального параметра в самом блоке impl:

struct Wrapper<T> {
    value: T,
}

impl<T> Wrapper<T> {
    fn get(&self) -> &T {
        &self.value
    }
}

Обратите внимание на impl<T> перед Wrapper<T>. Это сообщает Rust, что T является параметром обобщённого типа для всего блока реализации. Без этого объявления Rust будет искать конкретный тип с именем T и не сможет его найти.

Метод get возвращает &T — ссылку на любой тип, который содержит обёртка. Это работает независимо от того, является ли T целым числом, строкой или любым другим типом:

let num_wrapper = Wrapper { value: 100 };
let text_wrapper = Wrapper { value: "Rust" };

println!("{}", num_wrapper.get());  // 100
println!("{}", text_wrapper.get()); // Rust

Одно и то же определение метода работает для обоих случаев, так как универсальный T адаптируется к каждому конкретному типу во время компиляции.

challenge icon

Задание

Легко

Давайте расширим ваш универсальный контейнер с помощью методов! Вы создадите структуру Box (не путайте со стандартным Box в Rust), которая может хранить любой тип и предоставляет методы для взаимодействия с ее содержимым.

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

  • mybox.rs: Определите публичную универсальную структуру с именем MyBox<T> с приватным полем contents типа T. Реализуйте методы для этой структуры:
    • Связанную функцию new, которая создает новый MyBox с заданным значением
    • Метод peek, который возвращает ссылку на содержимое (используя &self)
    • Метод replace, который принимает новое значение и заменяет текущее содержимое (используя &mut self)
  • main.rs: Подключите свой модуль и продемонстрируйте работу универсальных методов с различными типами. Вы будете создавать боксы, просматривать их содержимое и заменять значения, чтобы показать методы в действии.

Помните ключевой синтаксис для реализации методов в универсальной структуре: вам нужно указать impl<T> перед MyBox<T>, чтобы сообщить Rust, что T является универсальным параметром для всего блока реализации.

В вашем основном файле продемонстрируйте работу MyBox следующим образом:

  1. Создание бокса с целым числом (первый ввод, преобразованный в i32)
  2. Просмотр его содержимого и вывод значения
  3. Замена содержимого новым целым числом (второй ввод, преобразованный в i32)
  4. Повторный просмотр для отображения обновленного значения
  5. Создание второго бокса со строкой (третий ввод)
  6. Просмотр содержимого строкового бокса

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

Integer box contains: {value}
After replace: {value}
String box contains: {value}

Например, при входных данных 10, 25 и Rust:

Integer box contains: 10
After replace: 25
String box contains: Rust

Вы получите три входных значения: начальное целое число, целое число для замены и строковое значение.

Шпаргалка

Чтобы определить методы для универсальной (generic) структуры, объявите универсальный параметр в блоке impl:

struct Wrapper<T> {
    value: T,
}

impl<T> Wrapper<T> {
    fn get(&self) -> &T {
        &self.value
    }
}

Синтаксис impl<T> сообщает Rust, что T является универсальным параметром типа для всего блока реализации. Без этого Rust искал бы конкретный тип с именем T.

Методы могут возвращать ссылки на универсальный тип (&T), что работает для любого конкретного типа:

let num_wrapper = Wrapper { value: 100 };
let text_wrapper = Wrapper { value: "Rust" };

println!("{}", num_wrapper.get());  // 100
println!("{}", text_wrapper.get()); // Rust

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

mod mybox;

use mybox::MyBox;

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

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

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

    // TODO: Создать MyBox с начальным целым числом
    
    // TODO: Посмотреть содержимое и вывести: "Integer box contains: {value}"
    
    // TODO: Заменить содержимое на новое целое число
    
    // TODO: Снова посмотреть содержимое и вывести: "After replace: {value}"
    
    // TODO: Создать второй MyBox со строковым значением
    
    // TODO: Посмотреть содержимое строкового бокса и вывести: "String box contains: {value}"
}
quiz iconПроверьте себя

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

Все уроки раздела Object Oriented Programming