Фото на обложке Маркус Списке на Unsplash
Отказ от ответственности: Я работаю в Keyrock, но высказанные здесь мнения являются моими собственными. Однако, если вам нравятся эти взгляды, посетите компанию, поскольку в ней много замечательных людей, и я многому научился, работая в ней, в том числе и Rust.
Если вы еще не читали части 1 и 2, настоятельно рекомендую начать с них:
Часть 1
- Синтаксис
- Разделение данных и кода
- Безопасность типов
- Декларативное программирование и особенности функциональной композиции
Часть 2
- Развитое управление пакетами
- Готовность к параллелизму std lib и отличная поддержка async
- Понятные, полезные ошибки компилятора
- Абстракции с нулевой стоимостью (Оптимизация)
- Энергоэффективность
- WASM
- Tauri — высокопроизводительные, безопасные, кроссплатформенные настольные вычисления
Часть 3
- Макросы!
- Корпоративное взаимодействие
- Криптовалюта и блокчейн
- Все остальные инструменты
- Скорость
- Отсутствие сборки мусора
- Функции безопасности памяти
Макросы!
Некоторые люди могут быть уязвлены и измучены бесконтрольным использованием макросов в некоторых языках (особенно низкоуровневых). В мире Ruby люди осуждали разработчиков «Rails», а позже во вселенной Elixir людям рекомендовали начинать без Phoenix, этой гигантской макросоориентированной кодогенерирующей красоты, опасаясь, что они никогда не изучат язык должным образом и не станут разработчиками «Phoenix»; я понимаю почему — но в этих примерах — как и в метапрограммировании Elixir (использование макросов для генерации и изменения кода) — макросы в целом использовались во благо и улучшали язык правильным образом. Обычно.
Макросы в Rust обычно очень разумны, и их легко идентифицировать — либо с их идентификаторами с восклицательным знаком для макросов декларативного стиля (как println!()
в std lib), либо используемые с derive
или как атрибуты пользовательских функций, которые обычно генерируют код из вашего кода.
В целом, макросистема является платформой для расширения функциональности и упрощения интерфейса к библиотекам. Это намекает на потенциальный рост, который еще впереди.
Так каким же образом это является важным фактором? Я считаю, что макросы очень похожи на систему пользовательских типов. Если тип хорошо документирован и открыто распространяется, он становится частью языка и общего знания системы, его знают все, кто претендует на звание ее приверженца. Если макросы не поддерживаются, плохо тестируются, плохо документируются и являются частью закрытой системы, они могут стать барьером в знаниях для тех, кто пытается изменить/исправить/протестировать код этой системы. — Отсюда и сопротивление многих людей.
Сообщество Rust, благодаря общей практике, установило довольно большие ожидания для людей, создающих макросы, которые становятся частью общего знания языка: Например, библиотека Serde, известная практически всем, кто использует Rust для разработки веб-сервисов, представляет собой тщательно документированный набор макросов, позволяющих осуществлять очень детальное преобразование при сериализации и десериализации данных. Существует даже «книга» по Serde. При таком дисциплинированном подходе макросы Rust в ближайшие годы обеспечат взрыв метапрограммирования, который усилит существующий рост Rust.
Дополнительная информация:
https://serde.rs/
https://happens.lol/posts/a-love-letter-to-rust-macros/
https://jstrong.dev/posts/2020/productive-rust-implementing-traits-with-macros/
Корпоративное взаимодействие
Мы прошли начальный этап постепенного роста компании Rust. На данный момент мы приближаемся к гиперросту. Попробуйте найти компанию, заинтересованную в программировании производительных систем, которая не рассматривала бы Rust или не начала бы изучать его самостоятельно. Учитывая, что Microsoft и AWS спонсируют крупные пакеты Rust, кажется, что все утки уже выстроились в ряд и готовы к взлету.
Давайте немного посмотрим, как это происходит и как выглядит это корпоративное участие:
«Мы не просто нанимаем пару человек для Tokio и компилятора Rust», — говорит Шейн Миллер. «Rust — важнейший компонент нашей долгосрочной стратегии, и мы вкладываем средства в разработку Rust в масштабах Amazon. Это включает инструменты для разработчиков, компоненты инфраструктуры, совместимость и проверку».
— AWS Open Source Blog, ноябрь 2020 г.
Заголовок новости:
«Шейн Миллер из AWS возглавил недавно созданный Rust Foundation».
— ZDNet, апрель 2021 г.
Программа грантов сообщества Rust Foundation стала возможной благодаря щедрой поддержке корпоративных партнеров, среди первых жертвователей — Amazon Web Services (AWS), Google и Huawei…
«Большая часть нынешнего успеха проекта Rust — заслуга активного сообщества и добровольцев, которые вносят свой вклад не только в решение инженерных задач, но и во все аспекты успешной работы проекта», — сказал Ларс Бергстром, директор по инженерным вопросам Google. «Мы считаем, что один из способов, с помощью которого Фонд Rust может наилучшим образом поддержать проект Rust, — это предоставление грантов сообщества этим крайне важным волонтерам».
— Businesswire, декабрь 2021 г.
Заголовок новости:
Команда Google Android Team поддерживает усилия по внедрению Rust в качестве второго языка программирования в ядро Linux.
— ZDNet, апрель 2021 г.
Заголовок новости:
In Rust we trust: Укрепляя Apache, ISRG отказывается от C и обращается к вундеркинду Лангу для нового криптомодуля TLS.
Подзаголовок: Старший сервер httpd будет возрожден с помощью финансируемого Google дополнения, обеспечивающего безопасность памяти
— The Register, февраль 2021 г.
Заголовок новости:
Fuchsia OS широко использует Rust для написания кода
«Согласно общему анализу кода Fuchsia, Rust содержит больше всего строк кода».
— Infotech News, meterpreter, май 2021 г.
Заголовок новости:
«Объявление трех новых членов правления Rust Foundation».
Подробности статьи:
Rust Foundation рад объявить, что три новых человека вошли в состав Совета директоров.
Члены:
— Райан Левик, представитель разработчиков, Microsoft
— Эндрю Вафаа, старший директор по программным сообществам, Arm
— Эрик Гарсия, менеджер по разработке программного обеспечения, Meta
— Новостной блог Rust Foundation, март 2022 г.
Это лишь малая часть новостей о Rust. Кроме того, все технологические гиганты запускают свои собственные материалы по внедрению и обучению Rust. Как идея, внедрение Rust на гигантском предприятии может показаться экспериментальным, но когда все делают это, что ж, в худшем случае это модная тенденция, а в лучшем — корпорации, которые вступают в игру, чтобы позиционировать себя вокруг технологии, составляющей часть основного конкурентного преимущества завтрашних решений.
Дополнительная информация:
https://docs.microsoft.com/en-us/learn/paths/rust-first-steps/
https://developer.ibm.com/articles/os-using-rust/
Криптовалюта и блокчейн
Если предположить, что вы не были заперты на черном сайте ЦРУ в течение последних 5 лет, вы, вероятно, видели еще одну неудержимую приливную волну, помимо Rust; называйте это как хотите: Web 3.0, революция блокчейна, подъем DEFI, NewFI, GameFI, новый фиат… Криптовалюта пришла, и она здесь, чтобы остаться (примечание: я могу быть немного предвзятым — см. мою историю в части 1). А что еще появилось вместе с этой новой индустрией? Спрос на производительность: Производительность обработки транзакций/блоков, производительность обработки исторических данных, производительность выполнения контрактов — все на различных распределенных платформах, каждая из которых претендует на превосходство над другими, все сжигают миллионы энергии (см. «Энергоэффективность» в части 2), и каждая не хочет, чтобы живой код вышел из строя (см. «Функции безопасности», ниже) — и все это для того, чтобы некоторые другие взволнованные люди могли купить мультяшных обезьян за $2 млн (два миллиона долларов) или кусок виртуальной земли и т.д. и т.п. Если бы вы начинали один из этих проектов сегодня, на какую технологию вы решили бы сделать ставку?
Вот подсказка — посмотрите Substrate, блокчейн-фреймворк, написанный на языке Rust.
Некоторые блокчейн-проекты, использующие Rust:
Название статьи:
«Solana использует Rust, чтобы привлечь разработчиков и избежать копипасты».
- The New Stack, январь 2022 г.
Заголовок новости:
«Concordium запускает инициативу DevX для поддержки разработчиков на Rust».
- CoinQuora, апрель 2021 г.
А когда многоименная, многоликая гидра социальных данных захотела создать криптовалюту (Libra/Diem), они тоже перешли на Rust, еще в 2019 году.
Инструментарий всех остальных
В какой-то момент можно привести наивный аргумент: «Так почему же все не используют Rust?». — Ну, есть много причин: Унаследованный код (Cobol, C++, который никто не хочет трогать, но хочет расширить — см. все Банки), доступность разработчиков, возможности инженерных менеджеров (?), контекст и требования (нам нужен быстрый код, или нам нужно быстро кодить? ), соответствие приложения контексту (Некоторые front-end фреймворки очень зрелые — зачем исправлять то, что не сломалось?), наличие специализированных библиотек (например, Python/Julia), приоритет архитектуры (например, мы хотим 100% функциональность, с акторами), зависимость от боли (Haskell, Java, C++) и т.д. и т.п.
Однако инструментарий вокруг других языков не обязательно должен быть ограничен ограничениями этих языков. Например, сочетание требований энергоэффективности, скорости и возможностей параллелизма может привести к тому, что кто-то напишет компилятор/интерпретатор для другого языка на Rust ? . Что еще более интересно, как только появится возможность транспонировать язык в Rust, его можно будет перекомпилировать в WASM и запустить в браузере!
Это означает, что Rust, скорее всего, станет основой инструментария для других языков, когда мы вступим в следующее десятилетие.
Примеры:
Интерпретатор Node.js, написанный на Rust: https://deno.land/.
Конвейер веб-инструментов, написанный на Rust: https://swc.rs/
Самая быстрая библиотека фреймов данных со ссылками на Python, написанная на Rust: https://pola-rs.github.io/polars-book/user-guide/index.html
Интерпретатор Clojure, написанный на Rust: https://github.com/clojure-rs/ClojureRS
Интерпретатор Python, написанный на Rust: https://rustpython.github.io/
Erlang BEAM (VM), написанный на Rust: https://getlumen.org/
Итак, теперь мы переходим к «большой тройке». Часто, когда я нахожусь вне дома и профессионально участвую в местных требованиях по поддержке тестирования операций по ферментации зерна, картофеля и фруктов, меня часто спрашивают: «Почему Rust?». Поскольку предыдущие 15 факторов в этих статьях потребовали бы довольно продолжительных технических дискуссий, которые могут быть сложными, учитывая мою ранее упомянутую профессиональную деятельность, и не говоря уже о том, что они чрезвычайно скучны для неразработчиков, я обычно останавливаюсь на следующих трех факторах.
Скорость
Может быть очевидно, что компилируемые языки низкого уровня быстрее; но может быть не очевидно, что они быстрее — я имею в виду в сотни, а иногда и в тысячи раз быстрее интерпретируемых языков (я не знал, что это не общеизвестно, пока недавно не поговорил с разработчиком JS, который сказал: «Тысячи? Ты уверен?»). Скорость Rust в целом находится на одном уровне с C++, а в некоторых случаях и выше — обычно, когда C++ не «жульничает» с тем, что Rust назвал бы небезопасным кодом. В мире Rust есть правила, согласно которым это добавит несколько наносекунд накладных расходов. Для большинства приложений разница будет спорной.
Более интересной является точка пересечения высокоуровневых абстракций (см. часть 1) и скорости. Это позволяет создавать очень быстрый код, который легко поддерживать, исправлять, тестировать и изучать (передача знаний в команде).
Дополнительная информация:
https://www.techempower.com/benchmarks/
https://www.logdna.com/blog/coding-for-performance-why-we-chose-rust
https://programming-language-benchmarks.vercel.app/c-vs-rust
https://programming-language-benchmarks.vercel.app/cpp-vs-rust
https://dev.to/deepu105/concurrency-in-modern-programming-languages-rust-vs-go-vs-java-vs-nodejs-vs-deno-36gg
Отсутствие сборки мусора
Интерпретированные или скомпилированные языки высокого уровня обычно очень безопасны для памяти благодаря общепринятой языковой функции сборщика мусора. Память отслеживается, и когда переменные больше не находятся в области видимости, память очищается, и мы никогда не видим нулевого указателя или ошибки сегментации/нарушения доступа к памяти. На самом деле большинство людей, владеющих языками высокого уровня, с которыми я разговариваю на эту тему, каждый раз говорят одно и то же: Мне никогда не приходилось задумываться о памяти, безопасности памяти или о том, как я использую память. Вы можете поблагодарить своего дружелюбного соседа сборщика мусора.
Так в чем же проблема? Сборщику мусора JavaScript в некоторых случаях требуется около 150 мс, чтобы закончить «зачистку» памяти. Это очень много времени в системе реального времени. Go-lang утверждает, что имеет самый быстрый сборщик мусора, и им удалось устранить большие паузы в сканировании памяти; они утверждают, что им удалось сократить паузу GC до 100 микросекунд. Это впечатляет и, вероятно, делает паузы GC несущественными для большинства контекстов.
Однако, что если мы создаем автопилот для самолета или самоуправляемый автомобиль? Нужны ли нам вообще GC-паузы? Если только мы не любим делать чрезвычайно опасные (и граничащие с психозом) азартные игры, скорее всего, нет [Есть исключения, в случае хорошо спроектированной системы, где GC распределен и контролируется, например, Pony — но они начинаются со слов «Если вы знаете, что делаете…»]. Точно так же большинство систем высокочастотной торговли в традиционном мире финансового трейдинга написаны на C++ по той же причине. Даже пауза в 100 микросекунд может привести к значительным потерям от этого периода «слепоты».
Это звучит здорово, и вы можете подумать: «Конечно, но я делаю веб-сервер, поэтому не думаю, что мне нужно беспокоиться об этих GC вещах». Возможно, вы правы — за исключением, может быть, еще одного аспекта; я собираюсь попасть в чье-то бинго — машинное сочувствие. Отсутствие сборщика мусора делает код более эффективным, как видно из энергетического профиля Rust, потому что память не сканируется периодически/выделяется/перераспределяется и т.д. Это можно понять интуитивно: пауза GC — это падение производительности, которое происходит потому, что (обычно) все потоки должны быть приостановлены (остановка мира), чтобы гарантировать, что во время паузы не произойдет доступа к памяти [За исключением вселенных Erlang/Elixir и Pony, конечно]. Многоядерные системы могут даже испытывать пагубное влияние на пропускную способность при увеличении числа потоков (так как время, затрачиваемое на сбор мусора, увеличивается).
Некоторые популярные варианты избежать GC — это C/C++/Rust/Objective-C/Swift/некоторые другие древние языки, такие как Cobol/Pascal/Fortran. Objective-C и Swift обычно рассматриваются как языки для устройств Apple с сопутствующими/релевантными инструментами (хотя Swift на стороне сервера, по-видимому, уже вещь), но не как кроссплатформенные языки общего использования (пока) — и подсчет ссылок в Swift рассматривается как тип GC (компилятор вставляет код release/retain во время компиляции), так что мы можем их удалить. Итак, после исключения древних языков остаются C/C++… и Rust.
Принимая во внимание все остальные факторы, какой бы вы выбрали?
Больше информации:
https://groups.google.com/g/golang-dev/c/Ab1sFeoZg_8/m/_DaL0E8fAwAJ?pli=1
https://www.oracle.com/java/technologies/javase/gc-tuning-6.html
https://wiki.c2.com/?LanguagesWithoutGarbageCollection
https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html
Особенности безопасности
Rust обеспечивает невероятную для низкоуровневого языка безопасность кода (больше, чем просто упущенное отслеживание времени жизни объектов, которое часто упоминается как главная особенность). Первая линия защиты исходит от сильной статической типизации, которая используется для проверки всех случаев использования типов во время компиляции. Аналогично, система трейтов создает структурированный способ проверки характеристик общих типов в «динамическом» коде, то есть функция может принимать общий тип, если общий тип выражает (или реализует в терминологии Rust) определенные трейты. Простой пример: функция println!()
(которая на самом деле является макросом std-lib) может принимать любой тип, но чтобы вывести его на stdout, тип должен реализовать либо трейты Display, либо трейты Debug (которые имеют разное использование, но с тем же макросом). Это означает, что код никогда не завершится из-за того, что в этот макрос был отправлен тип, не подлежащий отображению — потому что такой код вообще не компилировался бы!
Безопасность памяти обеспечивается функцией, называемой владением (Ownership). Теория владения состоит из двух частей: никакие две части кода не должны иметь возможности записывать в один и тот же участок памяти одновременно, и никакой код не должен иметь возможности получить доступ к участку памяти, который больше не находится в области видимости его родительской/вызывающей функции — или, скорее, «жив» достаточно долго для использования (концепция времени жизни ссылок). В основном это выражается в «передаче» прав собственности на переменные от одной части кода к другой, а также в отслеживании времени жизни ссылки во время компиляции. Ссылки также могут быть заимствованы, поэтому их можно читать, но нельзя записывать, если только они не заимствованы как mutable, что является альтернативой владению. Как вы можете себе представить, эта совершенно новая парадигма быстро становится сложной при первом знакомстве с ней и часто является источником разочарования для новичков в rust.
Я думаю, что в таком столкновении с новой парадигмой есть нечто интересное. Мы предполагаем, что знаем, как работает код. Мы сталкиваемся с новым языком и ищем «крючки» — знакомые парадигмы и философии, или архитектуры, которые мы можем использовать в качестве основы для обучения. А тут появляется новая, уникальная и незнакомая парадигма. Если мы не сделаем шаг назад и не признаем, что не знаем, как работает эта функция, и у нас почти нет основы для ее понимания, мы начнем спотыкаться и делать ошибки, или, что еще хуже, полностью избегать этой области языка, потому что ее трудно понять.
Так стоит ли изучать все эти вещи? Действительно ли Rust безопаснее? Пара примеров и доказательство:
i.
Ник Мэтьюсон, сооснователь проекта Tor, начал изучать Rust, перестраивая Tor с помощью Rust. Вскоре он обнаружил, что:
«Примерно половина проблем безопасности Tor с 2016 года была бы невозможна в Rust, а многие другие проблемы были бы гораздо менее вероятны, исходя из нашего неформального аудита».
Сейчас существует проект Tor на базе Rust (ARTI), который находится в разработке.
ii.
Компания Microsoft провела исследование, которое стало почти культовым в сообществе Rust. Они изучили ошибки и уязвимости в своем программном обеспечении, начиная с 2006 года, и заявили, что:
«примерно 70% проблем безопасности, которым MSRC присваивает CVE, являются проблемами безопасности памяти. Это означает, что если бы это программное обеспечение было написано на языке Rust, 70% этих проблем безопасности, скорее всего, были бы устранены».
Примечание: CVE = «Common Vulnerabilities and Exposures».
Доказательство:
Помимо простых рассуждений вокруг описания этих функций, откуда мы знаем, что они способствуют «безопасности» — в частности, безопасности памяти в языке. Можно написать множество тестов и создать множество различных сценариев, чтобы продемонстрировать это, но можно пойти дальше (если очень захотеть): В 2018 году Ральф Юнг, Жак-Анри Журдан, Робберт Кребберс и Дерек Дрейер пошли дальше и задокументировали формальное доказательство безопасности языка, включающее условия проверки для библиотек, которые, даже если они содержат небезопасные функции кода, могут быть использованы для демонстрации их безопасности и включения в качестве расширения языка.
Это важно, потому что без этих условий проверки библиотеки, содержащие C++, могут рассматриваться как изначально небезопасные. Если существует «внешний» набор условий, которым должна удовлетворять библиотека, ее включение в экосистему становится менее спорным процессом — и может обеспечить гораздо больше пересечений с другими низкоуровневыми языками системного программирования, имеющими десятилетия истории решения проблем, которые, возможно, еще не решены в Rust.
Дополнительная информация:
https://doc.rust-lang.org/nomicon/ownership.html
https://devopedia.org/rust-language
https://cacm.acm.org/magazines/2021/4/251364-safe-systems-programming-in-rust/fulltext
https://people.mpi-sws.org/~dreyer/papers/rustbelt/paper.pdf
https://plv.mpi-sws.org/rustbelt/
https://etaps.org/user-profile/archive/47-etaps-2018/etaps-2018/373-speaker-6
https://pvs-studio.com/en/blog/posts/0733/
В заключение
Является ли Rust моим любимым языком? Нет. Есть много областей для улучшения: Чрезмерно неявные паттерны (From/Into, неявные возвращаемые значения, которые легко пропустить, неявные свойства, такие как Display), некоторые более сложные внутренние архитектуры, которые требуют «приложить усилия» (например, Futures & «Pinning» памяти при использовании асинхронных самоссылающихся фьючерсов), значительная котельная пластина вокруг пользовательских Ошибок (некоторые библиотеки помогают, но также имеют свои собственные странные причуды) — я могу продолжить — но ни одна из них не является разрушительной. Эти вещи улучшаются с приемлемой скоростью преданной основной командой и огромным, мотивированным глобальным сообществом разработчиков (rustaceans ?).
Революция Rust заключается не только в том, что Rust — это очень приятный язык для изучения и написания кода — на самом деле, иногда бывает наоборот, когда вы только начинаете. Rust — это решение, которое удовлетворяет несколько (около 18) потребностей, соединяет парадигмы программирования и предоставляет достаточно дополнительных преимуществ и инструментов, чтобы не рассматривать его для всего, начиная от WASM, Data Science, производительных распределенных вычислений и встраиваемых систем.
Так куда же мы движемся? Примерно три года назад, когда я был фанатом этой технологии, не имея четкого представления о ней, один из моих коллег, занимающихся JavaScript, оспорил мою страсть и убежденность в Rust. Он спросил: «Так ты думаешь, что в ближайшие 5 лет все будут использовать Rust?». Я ответил: «Более или менее, да, я думаю, это возможно». Он сказал: «Хотите поспорить?». Сегодня я очень жалею, что не принял это пари. Последним языком, который имел такую широту охвата, и который также поддерживался сообществом… был JavaScript. И это то, к чему, как мне кажется, мы идем с Rust: Ubiquity; Rust скоро будет развиваться повсеместно.