Абстракции с нулевой стоимостью
Typestate — также хороший пример абстракций с нулевой стоимостью (zero cost abstractions): возможности перенести определённое поведение на этап выполнения компилятора или на этап анализа. Эти typestate не содержат никаких реальных данных и используются как маркеры. Поскольку они не содержат данных, у них нет реального представления в памяти во время выполнения:
use core::mem::size_of;
let _ = size_of::<Enabled>(); // == 0
let _ = size_of::<Input>(); // == 0
let _ = size_of::<PulledHigh>(); // == 0
let _ = size_of::<GpioConfig<Enabled, Input, PulledHigh>>(); // == 0
Типы нулевого размера
struct Enabled;
Структуры, определённые таким образом, называются типами нулевого размера (Zero Sized Types), поскольку они не содержат никаких реальных данных. Хотя на этапе компиляции эти типы ведут себя как «настоящие» — их можно копировать, перемещать, брать от них ссылки и т. д., — оптимизатор полностью удалит их.
В этом фрагменте кода:
pub fn into_input_high_z(self) -> GpioConfig<Enabled, Input, HighZ> {
self.periph.modify(|_r, w| w.input_mode().high_z());
GpioConfig {
periph: self.periph,
enabled: Enabled,
direction: Input,
mode: HighZ,
}
}
Возвращаемый GpioConfig никогда не существует во время выполнения. Вызов этой функции, как правило, сводится к одной ассемблерной инструкции — записи константного значения регистра в нужное место регистра. Это означает, что построенный нами интерфейс typestate — абстракция с нулевой стоимостью: он не тратит больше процессорного времени, ОЗУ или места в коде на отслеживание состояния GpioConfig, и компилируется в тот же машинный код, что и прямой доступ к регистру.
Вложенность
В общем случае такие абстракции можно вкладывать сколько угодно глубоко. Пока все используемые компоненты — типы нулевого размера, вся структура не будет существовать во время выполнения.
Для сложных или глубоко вложенных структур может быть утомительно определять все возможные комбинации состояний. В таких случаях для генерации всех реализаций можно использовать макросы.