Что вы узнаете: уроки из реального перевода примерно 100 тысяч строк C++ примерно на 90 тысяч строк Rust в составе около 20 крейтов. Пять ключевых паттернов преобразования и архитектурные решения, стоящие за ними.
Мы перевели большую диагностическую систему на C++ (около 100 тысяч строк C++) на реализацию на Rust (около 20 крейтов Rust, около 90 тысяч строк)
В этом разделе показаны реальные паттерны, которые использовались, — не игрушечные примеры, а настоящий продакшн-код
Пять ключевых преобразований:
№
Паттерн C++
Паттерн Rust
Эффект
1
Иерархия классов + dynamic_cast
Диспетчеризация через перечисления + match
~400 → 0 вызовов dynamic_cast
2
Дерево на shared_ptr / enable_shared_from_this
Арена + связи через индексы
Циклов ссылок нет
3
Сырой указатель Framework* в каждом модуле
DiagContext<'a> с заимствованием по времени жизни
Корректность ссылок проверяется на этапе компиляции
Неверный подход (буквальный перевод): сложить все события в одну разнородную коллекцию, а затем приводить типы — именно так C++ делает с vector<unique_ptr<Base>>
Верный подход: отдельные типизированные Vec устраняют всё понижающее приведение типов. Каждый потребитель запрашивает ровно тот тип события, который ему нужен
Производительность: отдельные Vec дают лучшую локальность кэша (все события degrade лежат в памяти непрерывно)
// Библиотека топологии на C++: PcieDevice использует enable_shared_from_this,
// потому что и родительский, и дочерние узлы должны ссылаться друг на друга
class PcieDevice : public std::enable_shared_from_this<PcieDevice> {
public:
std::shared_ptr<PcieDevice> m_upstream;
std::vector<std::shared_ptr<PcieDevice>> m_downstream;
// ... данные устройства
void AddChild(std::shared_ptr<PcieDevice> child) {
child->m_upstream = shared_from_this(); // Цикл родитель ↔ потомок!
m_downstream.push_back(child);
}
};
// Проблема: связи родитель→потомок и потомок→родитель создают циклы ссылок
// Чтобы разорвать циклы, нужен weak_ptr, но про него легко забыть