Вот почему мы выбрали Rust, препятствия, с которыми мы столкнулись, и коэффициент дохода от переписывания.

Rust незаметно стал одним из самых популярных языков программирования. Как развивающийся системный язык, Rust обладает многими характеристиками, такими как механизм безопасности памяти, преимущества в производительности, близкие к C/C++, отличное сообщество разработчиков, наборы инструментов и IDE.

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

Проект, который мы разрабатываем на Rust, называется KCL. (KCL) — это язык записи и функциональный язык с открытым исходным кодом, основанный на ограничениях. Он улучшает написание сложных конфигураций с помощью зрелого стека языков программирования. Он стремится к созданию лучшей модульности, масштабируемости и стабильности в отношении конфигурации, более простого написания логики, быстрой автоматизации и хорошей экологической расширяемости. Для более конкретных сценариев использования KCL посетите веб-сайт KCL.

KCL раньше был написан на Python. Учитывая пользовательский опыт, производительность и стабильность, мы решили переписать его на Rust и получили следующие преимущества:

  • Меньше ошибок благодаря мощной проверке компиляции и обработке ошибок в Rust.
  • На 66 % улучшена производительность сквозной компиляции и выполнения языка.
  • Производительность парсера интерфейса языка была улучшена в 20 раз.
  • Производительность семантического анализатора языка улучшена в 40 раз.
  • Среднее использование памяти компилятором языка во время компиляции составляет половину от исходной версии Python.

Проблемы, с которыми мы столкнулись

Компилятор, система сборки и среда выполнения используют Rust для выполнения аналогичных задач в проектах одного типа — таких как deno, swc, turbopack, rustc. Мы использовали Rust для создания передней, средней части и среды выполнения компилятора, но сделали это только около года назад.

Год назад мы использовали Python для создания всей реализации компилятора KCL. Хотя изначально он работал хорошо, поскольку Python был прост в использовании, эффективность исследований и разработок команды также была очень высокой. Однако с расширением кода и увеличением числа инженеров обслуживание кода стало более сложным.

Мы были вынуждены писать в проекте аннотации типа Python и приняли более строгие инструменты lint — и покрытие тестовыми строками кода тоже достигло более 90% — но по-прежнему много ошибок во время выполнения, таких как Python None пустые объекты, атрибуты не нашел и так далее. Мы должны быть осторожны при рефакторинге кода Python, который серьезно влияет на взаимодействие с пользователем.

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

Почему ржавчина?

Мы выбрали Rust по следующим причинам:

  • Мы использовали Python, Go и Rust для реализации простой виртуальной машины стека языков программирования и сравнили их производительность. В этом сценарии Go и Rust имеют одинаковую производительность, тогда как Python показал значительный разрыв в производительности. После тщательного рассмотрения мы выбрали Rust. Подробная информация о коде виртуальной машины стека, реализованном тремя языками, находится здесь: https://github.com/Peefy/StackMachine.
  • Все больше и больше компиляторов или сред выполнения языков программирования, особенно проектов инфраструктуры внешнего интерфейса, пишутся или рефакторингуются с использованием Rust. Кроме того, Rust появился в инфраструктурах, базах данных, поисковых системах, сетях, облачных технологиях, пользовательском интерфейсе, встроенных и других областях. По крайней мере, проверена возможность и стабильность реализации языков программирования.
  • Учитывая, что последующая разработка проекта будет включать направление блокчейна и смарт-контракта, а большое количество проектов блокчейна и смарт-контрактов в сообществе написано на Rust.
  • Благодаря Rust можно добиться лучшей производительности и стабильности, что делает систему более простой в обслуживании и более надежной. В то же время API-интерфейсы C могут быть доступны через FFI для многоязычного использования и расширения, что облегчает экологическое расширение и интеграцию.
  • Rust по-дружески поддерживает WASM. Rust создает большое количество экосистем WASM в сообществе. Языки и компиляторы KCL можно скомпилировать в WASM с помощью Rust и запустить в браузерах.

По вышеуказанным причинам мы выбрали Rust вместо Go. В процессе переписывания мы обнаружили, что всестороннее качество Rust действительно превосходно (высокая производительность и достаточная абстракция). Хотя некоторые функции языка имеют некоторую стоимость, особенно время жизни, экология могла бы быть богаче.

Трудности использования Rust

Хотя мы решили переписать весь проект KCL на Rust, у большинства членов команды нет опыта написания определенного проекта на Rust, а я только изучил Язык программирования Rust. Я смутно помню, что сдался, когда узнал об интеллектуальных указателях, таких как Rc и RefCell. В то время я не ожидал, что в Rust будет что-то похожее на C++.

Риск использования Rust — это в основном стоимость изучения языка, о чем действительно упоминается в различных блогах. Поскольку общая архитектура проекта KCL не сильно изменилась, а дизайн некоторых модулей и написание кода были оптимизированы для Rust, вся переработка была выполнена в процессе обучения и практики.

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

Ментальная трансформация

Во-первых, синтаксис и семантика Rust хорошо впитывают и интегрируют концепции, связанные с системой типов в функциональных программах, таких как абстрактный алгебраический тип (ADT).

Кроме того, в Rust нет понятия, связанного с наследованием. Если вам нужна помощь в понимании, то даже обычные определения структуры в Rust могут занять больше времени, чем в других языках. Например, следующий код Python будет определен в Rust иначе:

  • Питон
from dataclasses import dataclass

class KCLObject:
    pass

@dataclass
class KCLIntObject(KCLObject):
    value: int

@dataclass
class KCLFloatObject(KCLObject):
    value: float
  • Ржавчина
enum KCLObject {
    Int(u64),
    Float(f64),
}

Конечно, больше времени уходит на борьбу с сообщениями об ошибках самого компилятора Rust. Компилятор Rust часто заставляет разработчиков «упираться в стену», например, при заимствовании ошибок проверки. Специально для компилятора KCL его основной структурой является абстрактное синтаксическое дерево (AST), рекурсивная и вложенная древовидная структура.

Иногда сложно рассмотреть взаимосвязь между изменчивостью переменных и проверкой заимствования в Rust. Так же, как и структура области видимости Scope, определенная в компиляторе KCL, для сценариев с циклическими ссылками она используется для отображения взаимозависимости данных, о которой необходимо знать. при этом широко используются интеллектуальные структуры указателей, обычно используемые в Rust, такие как Rc, RefCell и Weak.

/// A Scope maintains a set of objects and links to its containing
/// (parent) and contained (children) scopes. Objects may be inserted
/// and looked up by name. The zero value for Scope is a ready-to-use
/// empty scope.
#[derive(Clone, Debug)]
pub struct Scope {
    /// The parent scope.
    pub parent: Option<Weak<RefCell<Scope>>>,
    /// The child scope list.
    pub children: Vec<Rc<RefCell<Scope>>>,
    /// The scope object mapping with its name.
    pub elems: IndexMap<String, Rc<RefCell<ScopeObject>>>,
    /// The scope start position.
    pub start: Position,
    /// The scope end position.
    pub end: Position,
    /// The scope kind.
    pub kind: ScopeKind,
}

Эффективность разработки

Эффективность разработки Rust можно описать как «сначала сдерживание, а затем улучшение». В начале рукописного проекта, если члены команды не знакомились с функциональным программированием и соответствующими навыками программирования, скорость разработки будет значительно ниже, чем у Python, Go, Java и других языков.

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

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

Кроме того, время жизни Rust связано с такими понятиями, как система типов, область действия, право собственности и проверка заимствования, что приводит к высокой стоимости и сложности понимания. Информация об ошибках часто не так очевидна, как ошибки типов.

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

struct Data<'a> {
    b: &'a u8,
}

// func2 omit lifecycle parameters, and func2 does not.
// The lifecycle of func2 will be deduced as '_ by the Rust compiler by default,
// which may lead to lifetime mismatch error.
impl<'a> Data<'a> {
    fn func1(&self) -> Data<'a> {Data { b: &0 }}
    fn func2(&self) -> Data {Data { b: &0 }}
}

Перепишите соотношение доходов с помощью Rust

После того, как несколько членов команды потратили месяцы, используя Rust, чтобы полностью переписать и стабильно ввести его в производственную среду в течение нескольких месяцев, мы пересмотрели весь процесс и сочли его очень полезным.

С технической точки зрения, процесс переписывания научил нас быстро изучать новый язык программирования и знания в области программирования и применять их на практике. Весь процесс перезаписи заставил нас задуматься о неразумной конструкции компилятора KCL и изменить его. Для языка программирования это проект с длительным циклом. Мы узнали, что система компилятора более стабильна и безопасна, с четким кодом, меньшим количеством ошибок и лучшей производительностью.

Хотя не все модули получают в 40 раз большую производительность (поскольку узким местом производительности некоторых модулей, таких как среда выполнения KCL, является операция глубокого копирования в память), я думаю, что это все же стоит. И когда Rust используется в течение определенного периода, разум и эффективность разработки перестают быть ограничивающими факторами.

Заключение

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

Наконец, если вам нравится проект KCL, вы хотите использовать KCL для своих собственных сценариев или хотите использовать Rust для участия в проекте с открытым исходным кодом, добро пожаловать на https://github.com/KusionStack/community. присоединиться к нашему сообществу для участия в обсуждении и совместном строительстве.

Ссылка