Создание синонимов типов с помощью псевдонимов типов
Rust позволяет объявлять псевдоним типа, чтобы дать существующему типу другое имя. Для этого мы используем ключевое слово type
. Например, мы можем создать синоним Kilometers
для i32
так:
type Kilometers = i32;
Теперь синоним Kilometers
является синонимом для i32
; в отличие от типов Millimeters
и Meters
, которые мы создали в Листинге 19-15, Kilometers
не является отдельным, новым типом. Значения, имеющие тип Kilometers
, будут обрабатываться так же, как значения типа i32
:
type Kilometers = i32;
let x: i32 = 5;
let y: Kilometers = 5;
println!("x + y = {}", x + y);
Поскольку Kilometers
и i32
являются одним и тем же типом, мы можем складывать значения обоих типов и передавать значения Kilometers
в функции, которые принимают параметры типа i32
. Однако, используя этот метод, мы не получаем преимущества проверки типов, которые мы получаем от нового типа, обсужденного ранее. Другими словами, если мы где-то перемешаем значения Kilometers
и i32
, компилятор не выдаст нам ошибку.
Основной случай использования синонимов типов - это уменьшение повторений. Например, у нас может быть длинный тип такого вида:
Box<dyn Fn() + Send + 'static>
Писать этот длинный тип в сигнатурах функций и в качестве аннотаций типов по всему коду может быть утомительно и подвержено ошибкам. Представьте, что у вас есть проект, полный кода, подобного тому, что в Листинге 19-24.
let f: Box<dyn Fn() + Send + 'static> = Box::new(|| {
println!("hi");
});
fn takes_long_type(f: Box<dyn Fn() + Send + 'static>) {
--snip--
}
fn returns_long_type() -> Box<dyn Fn() + Send + 'static> {
--snip--
}
Листинг 19-24: Использование длинного типа в многих местах
Псевдоним типа делает этот код более управляемым, уменьшая повторения. В Листинге 19-25 мы ввели синоним под названием Thunk
для длинного типа и можем заменить все использования типа на более короткий синоним Thunk
.
type Thunk = Box<dyn Fn() + Send + 'static>;
let f: Thunk = Box::new(|| println!("hi"));
fn takes_long_type(f: Thunk) {
--snip--
}
fn returns_long_type() -> Thunk {
--snip--
}
Листинг 19-25: Введение синонима типа Thunk
для уменьшения повторений
Этот код гораздо проще читать и писать! Выбор осмысленного имени для псевдонима типа может помочь передать ваше намерение (thunk - это слово для кода, который будет вычисляться позже, поэтому это подходящее имя для замыкания, которое сохраняется).
Псевдонимы типов также часто используются с типом Result<T, E>
для уменьшения повторений. Рассмотрим модуль std::io
в стандартной библиотеке. Операции ввода-вывода часто возвращают Result<T, E>
для обработки ситуаций, когда операции не могут быть выполнены. В этой библиотеке есть структура std::io::Error
, которая представляет все возможные ошибки ввода-вывода. Многие функции в std::io
будут возвращать Result<T, E>
, где E
- это std::io::Error
, например, эти функции в трейте Write
:
use std::fmt;
use std::io::Error;
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error>;
fn flush(&mut self) -> Result<(), Error>;
fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>;
fn write_fmt(
&mut self,
fmt: fmt::Arguments,
) -> Result<(), Error>;
}
Result<..., Error>
повторяется много раз. Поэтому в std::io
есть такое объявление псевдонима типа:
type Result<T> = std::result::Result<T, std::io::Error>;
Поскольку это объявление находится в модуле std::io
, мы можем использовать полностью квалифицированный синоним std::io::Result<T>
; то есть Result<T, E>
, в котором E
заполнено как std::io::Error
. Сигнатуры функций трейта Write
в итоге выглядят так:
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>;
fn flush(&mut self) -> Result<()>;
fn write_all(&mut self, buf: &[u8]) -> Result<()>;
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()>;
}
Псевдоним типа помогает двумя способами: он делает код легче писать и дает нам единый интерфейс по всему std::io
. Поскольку это синоним, это просто другой Result<T, E>
, что означает, что мы можем использовать любые методы, которые работают с Result<T, E>
, а также специальный синтаксис, такой как оператор ?
.