Type-driven development в Rust, часть 2/5: как доверить компилятору проверку контрактов между компонентами

Продолжаем серию о type-driven development в Rust — подходе, при котором правила предметной области выражаются в типах, а код, нарушающий эти правила, не компилируется. Рассказывает Никита Тимофеенко, разработчик команды MXDR компании F6.
В первой части типы отвечали за данные: какие значения возможны, какие комбинации допустимы, в каком порядке идут переходы. Но кроме данных в программе есть компоненты, которые общаются между собой, и у каждой такой границы есть контракт. Вторая часть посвящена тому, как записать эти контракты в типах, чтобы их нарушение не компилировалось.
После неё вы сможете найти в своём коде enum с match на каждом вызове, который на самом деле изображает открытый набор реализаций, и заменить его трейтом; отличить в контракте вход от выхода и перестать делать параметром трейта то, что должна выбирать реализация; поднять в тип размеры, которые известны ещё до запуска.
Во второй статье представлены три механизма, каждый разобран по той же схеме «проблема -> решение -> хорошие практики -> как это используют известные крейты или std библиотека»:
traits — контракт вместо конкретной реализации: код-потребитель требует поведение, а не тип, и работает с любым, кто его реализовал; новая реализация — это новый
impl, а не правка общего кода;associated types — типы, которые выбирает реализация, а не вызывающий: у каждой реализации свои типы результата и ошибки, и в один общий тип они не сводятся. Разница между параметром трейта и ассоциированным типом — это разница между входом и выходом;
const generics — значение как параметр типа: размер известен компилятору, а не хранится в рантайме, и структуры разного размера — разные типы. Что можно и чего нельзя в const-параметрах на стабильном Rust.
Отдельно рассмотрим CGP (Context-Generic Programming): что делать, когда одному типу нужно несколько реализаций одного трейта, а правило когерентности разрешает одну — и как одна и та же логика собирается под разные контексты без dyn и без match.
Примеры — из биржевой торговли, как и во всей серии, но приёмы работают в любом домене со сложными состояниями и правилами их изменения. Словарь типов продолжает часть 1.
Вторая статья «Type-driven development в Rust. Часть 2/5: задаём контракты между компонентами» уже на GitHub. Там же — компилируемые примеры ко всем приёмам, включая compile_fail-тесты на каждое «это не скомпилируется» из текста.













