Трейты с параметрами
Часть раздела Объектно-ориентированное программирование путешествия по Rust на Coddy. Урок 33 из 61.
До сих пор наши методы трейта принимали только &self в качестве параметра. Но методы трейта могут принимать дополнительные параметры, так же как обычные методы. Это делает их гораздо более полезными для реальных операций.
При определении трейта вы можете включить в сигнатуры методов любые необходимые вам параметры:
trait Calculator {
fn add(&self, a: i32, b: i32) -> i32;
fn multiply(&self, a: i32, b: i32) -> i32;
}
Этот trait Calculator требует два метода, каждый из которых принимает два целых числа в дополнение к &self. Любой тип, реализующий этот trait, должен предоставить обе операции с точно такими сигнатурами.
Вот как struct может это реализовать:
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
trait определяет, какие параметры принимает каждый method, а реализация определяет, как используются эти параметры. Это позволяет разным типам выполнять одни и те же операции по-своему: например, LoggingCalc мог бы print каждую операцию перед возвратом result.
Задание
ЛегкоДавайте создадим набор инструментов для работы со строками, в котором разные processors смогут по-своему преобразовывать текст! Вы создадите trait TextProcessor с методами, принимающими параметры, а затем Implement его для двух разных типов processors.
Вы организуете код в трёх файлах:
processor.rs: Define публичный traitTextProcessorс двумя методами, принимающими параметры:repeat(&self, text: &str, times: u32) -> String: повторяет заданный текст указанное количество разtruncate(&self, text: &str, max_len: usize) -> String: сокращает текст до указанной максимальной длины
processors.rs: Create две публичные struct, которые реализуют ваш trait:SimpleProcessor: unit struct, которая повторяет текст, разделяя каждое повторение пробелами (например, "Hi", повторённое 3 раза, превращается в "Hi Hi Hi"), и сокращает его, просто обрезая до максимальной длиныFancyProcessor: unit struct, которая повторяет текст, разделяя повторения строкой " * " (например, "Hi", повторённое 3 раза, превращается в "Hi * Hi * Hi"), и добавляет "..." при сокращении, если текст был укорочен (только если исходный текст был длиннее max_len)
main.rs: Объедините модули и продемонстрируйте, как одни и те же методы trait дают разные results в зависимости от выбранного processor.
В основном файле Create оба processor и используйте inputs для их проверки. Вы получите три inputs: строку текста, количество повторений и максимальную длину.
Print четыре строки, показывающие поведение каждого processor:
Simple repeat: {result}
Simple truncate: {result}
Fancy repeat: {result}
Fancy truncate: {result}Например, для inputs Hello, 3 и 4:
Simple repeat: Hello Hello Hello
Simple truncate: Hell
Fancy repeat: Hello * Hello * Hello
Fancy truncate: Hell...Обратите внимание, что оба processor соответствуют одному и тому же контракту TextProcessor, но каждый по-своему преобразует текст. trait Define, какие параметры принимают методы, а каждая реализация решает, как их использовать!
Вы получите три inputs: текст для обработки, количество повторений (parse как u32) и максимальную длину для сокращения (parse как usize).
Попробуйте сами
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}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media PlayerПотренируйтесь самостоятельно: Онлайн-компилятор Rust