Обобщённые методы
Часть раздела 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 адаптируется к каждому конкретному типу во время компиляции.
Задание
ЛегкоДавайте расширим ваш универсальный контейнер с помощью методов! Вы создадите структуру 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 следующим образом:
- Создание бокса с целым числом (первый ввод, преобразованный в
i32) - Просмотр его содержимого и вывод значения
- Замена содержимого новым целым числом (второй ввод, преобразованный в
i32) - Повторный просмотр для отображения обновленного значения
- Создание второго бокса со строкой (третий ввод)
- Просмотр содержимого строкового бокса
Ваш вывод должен соответствовать следующему формату:
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}"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player