From и Into
Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 57 из 61.
Преобразование между типами — это распространенная задача в Rust. Трейт From предоставляет стандартизированный способ определения того, как один тип может быть создан из другого. Когда вы реализуете From, вы автоматически и бесплатно получаете трейт Into.
Для реализации From вы определяете, как создать ваш тип из другого типа:
struct Meters(f64);
struct Kilometers(f64);
impl From<Kilometers> for Meters {
fn from(km: Kilometers) -> Self {
Meters(km.0 * 1000.0)
}
}
Теперь вы можете выполнить преобразование, используя либо функцию from, либо метод into:
let distance = Kilometers(5.0);
// Явное использование From
let m1 = Meters::from(distance);
// Использование Into (работает, так как реализован From)
let distance = Kilometers(3.0);
let m2: Meters = distance.into();
Метод into() требует аннотации типа, так как Rust должен знать, в какой тип вы выполняете преобразование. Этот паттерн особенно полезен, когда у вас есть вспомогательные структуры, которые необходимо преобразовать в ваши основные типы — например, преобразование данных конфигурации в инициализированный объект или трансформация необработанных входных данных в валидированную структуру.
Задание
ЛегкоДавайте создадим систему конвертации температуры, используя трейт From! Вы создадите два типа температуры — Celsius и Fahrenheit — и реализуете преобразование между ними, используя стандартные трейты преобразования Rust.
Вы организуете свой код в двух файлах:
temperature.rs: Определите две публичные структуры типа newtype —CelsiusиFahrenheit— обе оборачиваютf64. Реализуйте трейтFromдля преобразованияCelsiusвFahrenheit, используя формулу:fahrenheit = celsius × 1.8 + 32.0. Также добавьте методvalueв каждую структуру, который возвращает внутреннее значениеf64.main.rs: Подключите ваш модуль температуры и продемонстрируйте оба подхода к преобразованию. Создайте значениеCelsiusиз предоставленных входных данных, затем преобразуйте его вFahrenheitс помощьюFahrenheit::from(). Создайте еще одно значениеCelsiusи преобразуйте его с помощью метода.into(). Выведите оба результата.
Формула преобразования из Цельсия в Фаренгейт: умножить на 1.8 и прибавить 32. Помните, что реализация From<Celsius> for Fahrenheit автоматически дает вам трейт Into<Fahrenheit> для Celsius совершенно бесплатно!
Ваш вывод должен отображать оба преобразования в следующем формате:
From: {celsius1}C = {fahrenheit1}F
Into: {celsius2}C = {fahrenheit2}FНапример, при входных данных 0 и 100:
From: 0C = 32F
Into: 100C = 212FИ при входных данных 25 и -40:
From: 25C = 77F
Into: -40C = -40FВы получите два входных значения: первое значение Цельсия (парсится как f64) и второе значение Цельсия (парсится как f64).
Шпаргалка
Трейт From предоставляет стандартизированный способ преобразования между типами. Когда вы реализуете From, вы автоматически получаете реализацию трейта Into бесплатно.
Чтобы реализовать From, определите, как создать ваш тип из другого типа:
struct Meters(f64);
struct Kilometers(f64);
impl From<Kilometers> for Meters {
fn from(km: Kilometers) -> Self {
Meters(km.0 * 1000.0)
}
}
Вы можете выполнять преобразование, используя либо функцию from, либо метод into:
let distance = Kilometers(5.0);
// Явное использование From
let m1 = Meters::from(distance);
// Использование Into (требуется аннотация типа)
let distance = Kilometers(3.0);
let m2: Meters = distance.into();
Метод into() требует аннотации типа, чтобы Rust знал, в какой тип вы выполняете преобразование.
Попробуйте сами
mod temperature;
use temperature::{Celsius, Fahrenheit};
fn main() {
// Чтение ввода
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let celsius1: f64 = input1.trim().parse().expect("Invalid number");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let celsius2: f64 = input2.trim().parse().expect("Invalid number");
// TODO: Создать значение Celsius из celsius1
// TODO: Конвертировать его в Fahrenheit, используя Fahrenheit::from()
// TODO: Создать еще одно значение Celsius из celsius2
// TODO: Конвертировать его в Fahrenheit, используя .into()
// TODO: Вывести результаты в формате:
// From: {celsius1}C = {fahrenheit1}F
// Into: {celsius2}C = {fahrenheit2}F
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player