Трейты с параметрами
Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 33 из 61.
До сих пор наши методы типажа принимали только &self в качестве параметра. Но методы типажа могут принимать дополнительные параметры точно так же, как и обычные методы — это делает их гораздо более полезными для реальных операций.
При определении типажа вы можете включить любые необходимые параметры в сигнатуры методов:
trait Calculator {
fn add(&self, a: i32, b: i32) -> i32;
fn multiply(&self, a: i32, b: i32) -> i32;
}
Этот типаж Calculator требует наличия двух методов, каждый из которых принимает два целых числа в дополнение к &self. Любой тип, реализующий этот типаж, должен предоставлять обе операции с этими точными сигнатурами.
Вот как структура может это реализовать:
struct BasicCalc;
impl Calculator for BasicCalc {
fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
fn multiply(&self, a: i32, b: i32) -> i32 {
a * b
}
}
Теперь вы можете использовать эти методы с аргументами:
let calc = BasicCalc;
println!("{}", calc.add(5, 3)); // 8
println!("{}", calc.multiply(4, 7)); // 28
Трейт определяет, какие параметры принимает каждый метод, в то время как реализация определяет, как эти параметры используются. Это позволяет различным типам выполнять одни и те же операции по-своему — возможно, LoggingCalc мог бы выводить каждую операцию перед возвратом результата.
Задание
ЛегкоДавайте создадим набор инструментов для манипуляции строками, где различные процессоры могут преобразовывать текст по-своему! Вы создадите типаж TextProcessor с методами, принимающими параметры, а затем реализуете его для двух различных типов процессоров.
Вы организуете свой код в трех файлах:
processor.rs: Определите публичный типажTextProcessorс двумя методами, принимающими параметры:repeat(&self, text: &str, times: u32) -> String— повторяет заданный текст указанное количество разtruncate(&self, text: &str, max_len: usize) -> String— укорачивает текст до указанной максимальной длины
processors.rs: Создайте две публичные структуры, реализующие ваш типаж:SimpleProcessor— unit-структура, которая повторяет текст с пробелами между повторениями (например, "Hi", повторенное 3 раза, становится "Hi Hi Hi") и усекает текст, просто обрезая его по максимальной длинеFancyProcessor— unit-структура, которая повторяет текст с " * " между повторениями (например, "Hi", повторенное 3 раза, становится "Hi * Hi * Hi") и усекает текст, добавляя "...", если текст был сокращен (только если исходный текст был длиннее max_len)
main.rs: Объедините ваши модули и продемонстрируйте, как одни и те же методы типажа дают разные результаты в зависимости от того, какой процессор вы используете.
В основном файле создайте оба процессора и используйте входные данные для их тестирования. Вы получите три входных значения: текстовую строку, количество повторений и максимальную длину.
Выведите четыре строки, показывающие поведение каждого процессора:
Simple repeat: {result}
Simple truncate: {result}
Fancy repeat: {result}
Fancy truncate: {result}Например, при входных данных Hello, 3 и 4:
Simple repeat: Hello Hello Hello
Simple truncate: Hell
Fancy repeat: Hello * Hello * Hello
Fancy truncate: Hell...Обратите внимание, как оба процессора выполняют один и тот же контракт TextProcessor, но каждый преобразует текст своим уникальным способом. Типаж определяет, какие параметры принимают методы, в то время как каждая реализация решает, как их использовать!
Вы получите три входных значения: текст для обработки, количество повторений (распарсить как u32) и максимальную длину для усечения (распарсить как usize).
Шпаргалка
Методы типажа могут принимать дополнительные параметры, помимо &self:
trait Calculator {
fn add(&self, a: i32, b: i32) -> i32;
fn multiply(&self, a: i32, b: i32) -> i32;
}
Реализация типажа с параметрами:
struct BasicCalc;
impl Calculator for BasicCalc {
fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
fn multiply(&self, a: i32, b: i32) -> i32 {
a * b
}
}
Использование методов типажа с аргументами:
let calc = BasicCalc;
println!("{}", calc.add(5, 3)); // 8
println!("{}", calc.multiply(4, 7)); // 28
Типаж определяет, какие параметры принимает каждый метод, в то время как реализация определяет, как эти параметры используются.
Попробуйте сами
mod processor;
mod processors;
use processor::TextProcessor;
use processors::{SimpleProcessor, FancyProcessor};
fn main() {
// Чтение входных данных
let mut text = String::new();
std::io::stdin().read_line(&mut text).expect("Failed to read line");
let text = text.trim();
let mut times_input = String::new();
std::io::stdin().read_line(&mut times_input).expect("Failed to read line");
let times: u32 = times_input.trim().parse().expect("Failed to parse times");
let mut max_len_input = String::new();
std::io::stdin().read_line(&mut max_len_input).expect("Failed to read line");
let max_len: usize = max_len_input.trim().parse().expect("Failed to parse max_len");
// Создание процессоров
let simple = SimpleProcessor;
let fancy = FancyProcessor;
// TODO: Используйте процессоры для преобразования текста и вывода результатов
// Выведите четыре строки:
// Simple repeat: {result}
// Simple truncate: {result}
// Fancy repeat: {result}
// Fancy truncate: {result}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player