Я просматривал стандартную библиотеку Rust и наткнулся на PhantomData. Я не знал, для чего он использовался, поэтому начал читать о нем больше.

Rust не позволит вам объявить параметр типа без его использования. Решение - PhantomData. Тип PhantomData можно использовать в ситуациях, когда вам действительно не нужно хранить значение чего-либо, а, скорее, просто тип. В документации по Rust есть несколько примеров, но я подумал, что попробую один для себя.

Последний рубеж 👩‍🚀

Допустим, вы хотите приземлиться на Марс и у вас есть три команды: Sensor, Thrust и System. Обязанности команды Sensor and Thrust довольно очевидны. Команда разработчиков системы отвечает за использование кода группы датчиков и тяги для программирования последовательности приземления.

Первая итерация 🚀

Совместными усилиями всех команд был получен следующий код.

Ладно, как вы понимаете, я не разбираюсь в ракетостроении. Несмотря на нулевое знание ракет, я все же могу сказать вам, что это будет неудачно - тяжело.

Числа без контекста 🇺🇸 🇸🇪 🇬🇧

Оказалось, что команды Sensor и Thrust находятся в разных странах, и это, оказывается, страны с разными системами измерения. Одна команда использует метры, а другая - футы, это нехорошо!

Каждая команда внутренне согласовывала единую систему измерения, но никогда между командами. Команда Sensor так и не осознала проблему, потому что никогда напрямую не взаимодействовала с кодом команды Thrust, и наоборот. Команда System использует код команды Sensor и Thrust, но имеет ограниченные знания о внутренней работе кода другой команды. Они с удовольствием используют код, как любую другую библиотеку.

Команды могут попробовать решить, какую единицу измерения и когда использовать, но это непростая задача. Команды большие, и многие люди вносят свой вклад в кодовые базы. Это человеческая ошибка, ожидающая своего часа.

Вторая итерация ♻️

Давайте рассмотрим другое решение, основанное на PhantomData.

Так что здесь происходит?

Нашел какой-то контекст 🕵️‍♀️

Вторая итерация сосредоточена вокруг типа Float64 ‹T›. Проблема с предыдущим кодом заключалась в том, что измерения отправлялись как обычный f64. У f64 действительно нет контекста, это может быть числовое представление чего угодно. Вместо этого мы создаем собственный тип с параметром типа T.

С параметром типа T мы можем сказать, что Float64 ‹T› фактически является Float64 ‹Meter›. Здесь на помощь приходит PhantomData. На самом деле мы не хотим объявлять поле t: T в нашем типе Float64, поэтому вместо этого мы используем поле PhantomData. Что приятно, так это то, что тип PhantomData имеет нулевой размер, поэтому мы получаем семантику, не занимая дополнительной памяти.

Помогите человеку 🤖

Обратите внимание, что каждая команда теперь получает и возвращает определенные версии Float64 ‹T›, некоторые работают только с метрами, а другие - с футами. Настоящая проблема здесь в том, что приведенный выше пример не будет компилироваться. Отлично :). Команда System теперь может быть более уверена в том, что они передают правильный тип ценностей и наконец приземляются на Марс.

Это действительно интересно. То, что могло стать ошибкой времени выполнения, теперь возвращается на этапе разработки и вместо этого выделяется как ошибка времени компиляции. В правильной ситуации это может быть исправление ошибок, качество кода и экономия времени.

Сахар Тип 🍯

Поскольку мы заключили обычный f64 в наш тип Float64, работать с ним может быть немного неудобно, например, при выполнении математических операций. Итак, в примере я добавил реализацию трейта std :: ops :: Mul для Float64 ‹T›, теперь мы можем использовать * (оператор умножения) с нашим типом оболочки. Если бы мы продолжили работу и реализовали больше черт std :: ops, мы могли бы рассматривать это почти так, как если бы это был обычный f64.

Некоторые мысли 🤔

Пока я искал решения для этого примера, я понял, что есть много способов решить проблему, и использование PhantomData может быть не самым очевидным или стандартным способом. Тем не менее, мне понравился этот мысленный эксперимент, и, надеюсь, некоторым из вас понравится :)

/ Роберт