﻿# Критика языка Rust и почему C/C\+\+ никогда не умрет

Нам очень понравилась статья "Критика языка Rust и почему C/C\+\+ никогда не умрет"\. Мы предложили автору, что выполним перевод статьи на английский язык, а также опубликовать её в нашем блоге\. Он согласился, и мы с удовольствием представляем эту статью на русском и английском языке\.

Я не мог не заметить, что читателей сего блога очень заинтересовала тема "нужно ли давать котикам играться с новыми клубочками"\. Поэтому хотелось бы поделиться еще кое\-какими своими мыслями по связанной теме, в отношении языков Си и C\+\+ и убьет ли их Rust\. Тема, как вы сами понимаете, очень\-очень холиварная, поэтому подумайте еще раз, хотите ли вы читать эту заметку дальше и тем более участвовать в "конструктивном обсуждении" поста при помощи комментариев\.

Оригинал статьи размещён [здесь](https://eax.me/cpp-will-never-die/) \(текст на русском языке\)\. Статья опубликована в нашем блоге с соглашения автора\.

_Примечание_: Ниже по тексту я исхожу из предположения, что Rust является попыткой сделать _быстрый_ и безопасный язык\. В конце концов, ребята из Mozilla делали его, как инструмент разработки [браузерного движка](https://github.com/servo/servo)\. Если же это очередной просто безопасный язык, тогда получаем странное\. Самых разных безопасных языков и так уже пруд пруди, каждый найдет себе по вкусу\. И если не стоит цели заменить C\+\+, то \(1\) для чего в языке сделано unsafe подмножество? \(2\) зачем было [удалять из языка легковесные потоки](https://stackoverflow.com/questions/29791031/what-happened-to-libgreen), удобно же? Другими словами, в этом случае происходящее вообще не имеет никакого смысла\.

Если вдруг вы почитываете форум linux\.org\.ru, отмечу, это это не тот список из 10 чисто технических причин не любить [Rust](https://eax.me/rust/), речь о котором [шла в этом трэде](https://www.linux.org.ru/forum/development/11513142)\. Как показало обсуждение в Skype с [уважаемым товарищем @sum3rman](https://twitter.com/sum3rman), есть больше одного мнения касательно того, насколько "техническими" считать эти причины\. В общем, фиговый список я составил, но кое\-какие пункты из него, наиболее интересные, пожалуй, я все\-таки рискну привести\. На самом деле, тут и простых, не технических, причин за глаза хватает\.

То, что C/C\+\+ в обозримом будущем никуда не денутся, и так любому трезво мыслящему человеку понятно\. Никто не станет переписывать почти все десктопные приложения, ядра операционных систем, компиляторы, игровые и браузерные движки, виртуальные машины, базы данных, архиваторы, аудио\- и видеокодеки, тонны прочих сишных библиотек, и так далее\. Это очень\-очень много быстрого, отлаженного, проверенного временем кода\. Переписывать его очень\-очень дорого, рискованно, и если честно, не лишено смысла только в искаженном сознании только самых упоротых Rust'оманов\. Спрос на C/C\+\+ программистов был и будет велик еще очень долго\.

Хорошо, а как на счет применения Rust при написании нового кода?

Вспомним, что это уже далеко не первая попытка сделать "более правильный" C/C\+\+\. Возьмем хотя бы язык D\. Появился в 2001 году, очень хороший язык\. Нет ни вакансий, ни нормальных инструментов разработки, ни каких\-то особо выдающихся саксесс сторис\. Проект [OpenMW](https://openmw.org/en/) изначально писали на D, а потом внезапно решили [целиком переписать на C\+\+](http://wiki.openmw.org/index.php?title=Project_History#Version_0.7.0)\. Как [признаются](https://forum.openmw.org/viewtopic.php?f=2&t=2189) разработчики, им приходило много писем в стиле "отличный проект, мы были бы рады в него контрибьютить, но не знаем и не хотим знать этот дурацкий D"\. Википедия сообщает, что помимо D была и масса других попыток в той или иной степени убить C\+\+, например, [Vala](https://eax.me/vala/), Cyclone, Limbo, BitC\. Многие ли вообще слышали о таких языках?

Думаю, давно пора извлечь уроки из истории\. Ни один здравомыслящий человек не потащит в проект новый язык, пока вы хотя бы не покажете ему нормальные инструменты разработки, не расскажете парочку саксесс сторис и не покажете десяток программистов на этом языке, живущих поблизости\. Программисты же, пожалуй, кроме самых молодых, никогда не станут тратить свое время и здоровье на изучение очередного самого правильного языка, пока вы не покажете им нормальные инструменты разработки \(не поделки типа Racer\), пару _десятков тысяч_ готовых библиотек \(не "experimental", "unstable" и так далее\), не расскажете парочку саксесс сторис и не покажите десяток открытых вакансий в их городе\. Проблема курицы и яйца\. Очень редко эту проблему удается успешно решить \(условно тут можно привести в пример [Go](https://eax.me/go-lang/) и [Scala](https://eax.me/scala/)\), в основном благодаря вложению времени и денег со стороны некоторой крупной компании \(Google, Typesafe\), по каким\-то своим соображениям заинтересованных в популяризации языка\.

Как я уже отмечал, одних только [не технических причин](https://eax.me/avoid-new-toys/) более, чем достаточно\. Однако чисто из любопытства попробуем представить на секунду, что их нет\. Тогда нет причин не писать на Rust? Оказывается, это тоже как минимум под очень большим вопросом\.

C/C\+\+ критикуют за разное\. Критикуют, кстати, очень часто те, кто в продакшене даже издали не видел кода на С\+\+\. Коротко и ясно проблему можно описать так: С\+\+ очень быстрый \(а также не требовательный к памяти, заряду батареи и тд\), но не безопасный в том смысле, что позволяет выходить за границы массивов, по ошибке обращаться к освобожденным кускам памяти и так далее\. В свое время эта проблема привела к появлению массы безопасных языков, таких, как Java, C\#, Python и других\. Но оказалось, что эти языки по сравнению с C\+\+ слишком требовательны к ресурсам и обладают прочими недостатками, вспомним хотя бы неизбежный stop the world при сборке мусора\. Поэтому люди бьются над задачей сделать язык такой же быстрый, как C\+\+, но еще и безопасный\. Одним из таких языков и является Rust\.

Rust действительно безопасный, но, к сожалению, далеко не быстрый\. По скорости на момент написания этих строк Rust сравним с Java, Go и Haskell:

![0324_Criticizing-Rust-Language-Why-Cpp-Will-Never-Die_ru/image1.png](https://import.viva64.com/docx/blog/0324_Criticizing-Rust-Language-Why-Cpp-Will-Never-Die_ru/image1.png)

Я искренне надеюсь, что со временем его как\-то разгонят, но до тех пор в плане компромисса скорости и безопасности он ненамного интереснее Scala или Go\. До сих пор остается открытым вопрос, можно ли вообще сделать язык быстрым и безопасным, или постоянные проверки на выход за границы массива, безопасные обвязки вокруг биндингов к сишным библиотекам и так далее автоматически делают любой язык в 2 раза медленнее С/C\+\+\.

А за счет чего, собственно, Rust безопасен? Если говорить простыми словами, то это язык со встроенным статическим анализатором кода\. Действительно очень крутым статическим анализатором, который ловит все типичные для С\+\+ ошибки, притом не только связанные с управлением памятью, но и многопоточностью\. Передал по каналу ссылку на изменяемый объект другому потоку, а потом попробовал воспользоваться этой ссылкой сам — все, не скомпилится\. Это действительно здорово\.

Но C\+\+ последние 30 лет тоже не стоял на месте, для него существует великое множество как [статических](https://pvs-studio.ru/ru/pvs-studio/), так и динамических анализаторов\. Посмотрите хотя бы короткое видео про [гугловые санитайзеры](https://www.youtube.com/watch?v=q2c2lp8_tne), они действительно _очень_ круты\. В любом серьезном проекте вы все равно используете [систему непрерывной интеграции](https://eax.me/jenkins/) и при сборке билда прогоняете кучу тестов\. Если это не так, то у вас куда более серьезные проблемы, чем небезопасность языка, потому что статическая типизация не гарантирует вам правильную логику работы приложения\! Так вот, а раз вы все равно прогоняете тесты, почему бы не воспользоваться еще и санитайзерами? Да, они находят не все баги\. Но с другой стороны, если где\-то глубоко в коде вы, условно говоря, не делаете проверку на выход за границу массива, и санитайзер не ловит баг, может быть это просто потому что выше уровнем уже есть все нужные проверки и дополнительная приведет только к лишним тормозам? И даже без санитайзеров сборка проекта разными компиляторами на разных платформах с assert'ами, проверяющими инварианты вашего кода в стиле "assert\(obj\-\>isValid\)", и хорошим fuzzing'ом найдет вам очень много чего\. Грубо говоря, вопрос сводится к старому\-доброму холивару на тему [те\-еретического и колхозного подхода](https://eax.me/kolkhoz-doctrine/) к разработке\.

Часто приводится аргумент, что [90% времени выполняется только 10% кода](https://en.wikipedia.org/wiki/Program_optimization#Bottlenecks) \(что, насколько я понимаю, чисто эмпирическое правило — быстро найти строгих исследований на эту тему не удалось\)\. Следовательно, бОльшую часть программы можно написать на безопасном Rust, а 10% "горячего" кода — на его unsafe подмножестве, и медленность текущей реализации Rust на самом деле не представляет собой проблемы\. Ок, но тогда получается, что Rust вообще не нужен, потому что я могу написать 90% кода на Go, а 10% на Си\. Только искатели серебряных пуль и оторванные от реальности те\-еретики будут использовать Rust исключительно из соображений, что 100% программы можно написать как бы на одном языке\. Хотя в действительности это два диалекта одного языка, что не так уж сильно отличается от связки Java плюс Си или Go плюс Си\.

На самом деле, правило 10:90 — это все равно вранье\. По этой логике можно переписать 90% WebKit, 90% VirtualBox или 90% GCC на Java и получить такой же результат\. Очевидно, это не так\. Даже если дело не в том, что в ряде программ это отношение очень другое, то следите за руками\. Допустим, вся программа написана на небезопасном C/C\+\+ и время ее выполнения, условно говоря, равно 0\.9\*1 \(малая часть горячего кода\) \+ 0\.1\*1 \(много холодного кода\) \= 1\. Теперь сравним с программой на безопасном языке со вставками на Си: 0\.9\*1 \+ 0\.1\*2 \= 1\.1, условно 10% разницы\. Это много или мало? Зависит от ваших масштабов\. В случае с Google даже несколько процентов могут [сэкономить миллионы долларов](https://research.google/pubs/pub43438/) \(см пункт 5 в пейпере, "Utilization"\)\. Или представьте, что со следующим обновлением JVM внезапно начнет требовать на 10% больше ресурсов\! Я боюсь даже гадать, сколько нулей будет в цифре, полученной после перевода процентов на американские деньги\! 10% — это дофига в задачах, где используются Си и C\+\+\.

Мы повторяем "преждевременная оптимизация — корень всех зол", как мантру\. Но если следовать ей буквально, то давайте повсюду использовать пузырьковую сортировку вместо quicksort\. Мы же не знаем точно, что программа будет именно в этом месте тормозить\! Какой смысл оборачивать обыкновенные счетчики каких\-тод ействий в акторы или транзакционную память, если можно сразу воспользоваться более эффективным atomic? И вообще, в _тривиальных_ _случаях_ нет смысла принудительно инициализировать все\-все\-все переменные, делать кучу дополнительных проверок и так далее\. Пусть в итоге мы получим не 10% ускорения, а 2\-5%\. Это ведь тоже совсем неплохо, если потребовало всего лишь пары лишних минут размышлений\. И как мы уже выяснили, в задачах, решаемых на С/C\+\+, это может быть большой разницей\! Потом, кто сказал, что найти горячее место, переписать код \(возможно, очень много кода\) и доказать, что он стал действительно быстрее — это проще, чем подумать о производительности заранее?

Если отвлечься от вопроса компромисса скорости и безопасности, то по дизайну самого языка у меня тоже есть вопросы\. В частности, касательно пяти типов указателей\. С одной стороны, это неплохо, когда программист задумывается о том, где лежат переменные, в стеке или куче, и могут или не могут с ними одновременно работать несколько потоков\. Но с другой, представьте, что вы пишите программу, и оказалось, что переменная должна жить не в стеке, а в куче\. Вы переписываете все, чтобы использовался Box\. Потому вы понимаете, что на самом деле нужен Rc или Arc\. Снова переписываете\. А потом еще раз переписываете на обычную переменную в стеке\. Все это — без нормальной IDE под рукой\. И регулярки не помогут\. Ну или же просто [получаете ужасы](https://www.reddit.com/r/rust/comments/33jv62/vecrcrefcellboxtrait_is_there_a_better_way/) в стиле "Vec<Rc<RefCell<Box<Trait\>\>\>\>", привет, Java\! Но что самое печальное, компилятор уже знает о времени жизни всех переменных, он мог бы выводить все эти Box, Arc и так далее автоматически\. Но почему\-то эта часть работы переложена на программиста\. Намного удобнее было бы просто писать val \(в третьем\-то тысячелетии\!\), а там, где надо, явно указывать Box или Rc\. Разработчики Rust в этом смысле запороли всю идею\.

Из\-за этого, в частности, сильно сужается область применения Rust\. Никто в здравом уме не станет писать на _таком_ языке веб и серверсайд\. Особенно учитывая, что он не дает существенных преимуществ перед теми же языками под JVM\. Да и Go с _нормальными_ легковесными потоками \(не [футурами](https://eax.me/scala-futures/)\) для этих задач выглядит куда более привлекательнее\. С футурами, чтобы не прострелить себе ногу, нужно еще научиться работать, а вы говорите "безопасный язык"\. Да, у этих языков свои особенности, взять все тот же stop the world, но эта проблема решаемая, как распиливанием на [микросервисы](https://eax.me/micro-service-architecture/), так и [другими приемами](http://www.bailis.org/blog/doing-redundant-work-to-speed-up-distributed-queries/)\. И да, никто не будет транслировать Rust в JavaScript, писать на нем скрипты для раскладки в AWS, или использовать в качестве языка запросов к MongoDB\. Под Android тоже вряд ли писать будут, но по другой причине — там сильно больше одной архитектуры, с JVM намного проще\. Если вы вдруг думали, что Rust "подходит для всех задач", вынужден вас огорчить\.

Ну и до кучи:

* Макросы, как подпорка к излишней многословности, вызванной отсутствием нормальных исключений\. Я уже писал о [проблемах метапрограммирования](https://eax.me/avoid-metaprogramming/), в частности, нормальную IDE для Rust мы вряд ли увидим из\-за него\. И я не уверен, но похоже, что у макросов в Rust даже неймспейсов нет\.
* Люди идиоты, а cargo очень поощряет стягивание пакетов напрямую из git\-репозиториев, в обход Crates\.io\. В итоге велика вероятность получить такой же бардак с пакетами, как и в мире Erlang с его Rabar'ом\. К слову, в мире Go, похоже, такая же ситуация\.
* Как многие новые языки, Rust идет по пути упрощения\. Я в целом понимаю, почему в нем нет нормального наследования и исключений, но сам факт, что кто\-то за меня решает такие вещи, оставляет неприятный осадок\. C\+\+ не ограничивает программиста в вопросах чем пользоваться, а чем нет\.
* Если уж идти по пути упрощения, то выкинуть бы уж все эти расширения языка\. А то получается, как в мире Haskell, каждый программист пишет на своем диалекте\.
* Смарт поинтеры, если что, далеко не бесплатны и _не приводят_ к предсказуемому времени сборки мусора\. Какому\-то потоку внезапно выпадает честь освободить очень глубокую структуру данных\. Пока он ходит по лабиринту из мертвых ссылок, зависящие от него потоки терпеливо тупят\. Та же проблема есть и в Erlang с его маленькими кучками, сам не раз наблюдал\. Смарт поинтеры имеют и свои проблемы, ту же фрагменатцию памяти и утечки\. Забыл викпоинтер в цеклической структуре, и все\. И это в языке, претендующем на безопасность\. Если хотите предсказуемого времени GC, либо изучайте поведение вашего приложения под нагрузкой, и предпринимайте меры \(вспомним хотя бы те же пулы объектов\), если время GC вас не устраивает, либо управляйте памятью вручную\.
* Кто\-нибудь видел _строгое_ описание семантики Rust? У него хотя бы memory model есть? Тоже мне "безопасный" язык, "доказывающий корректность" программ, который вообще\-то может трактовать исходный код десятью разными способами, ха\!
* Не могу в очередной раз не напомнить, что **проблема почти всегда в людях, а не в технологиях**\. Если у вас получается плохой код на C\+\+ или Java вдруг тормозит, это не потому что технология плоха, а потому что вы не научились правильно ею пользоваться\. Rust вы тоже будете недовольны, но уже по другим причинам\. Не проще ли научиться пользоваться более популярными инструментами и начать их любить?

В общем и целом, ближайшие лет 5 я лучше будут инвестировать свое время в изучение C/C\+\+, чем Rust\. С\+\+ — _это промышленный стандарт_\. На этом языке успешно решают самые разнообразные задачи уже более 30 лет\. А Rust и иже с ним — непонятные игрушки с туманным будущем\. Про скорую смерть С\+\+ разговоры идут как минимум с 2000\-х, но писать на C/C\+\+ за это время стали не меньше\. Скорее наоборот\. И мы видим, что язык развивается \(C\+\+11, C\+\+14\), для него появляются новые инструменты \(вспомним хотя бы [CLion](https://www.jetbrains.com/clion/) и Clang\), и соответствующих вакансий просто куча\.

Программист на C\+\+ всегда без труда найдет себе работу с более чем достойной зарплатой, а при необходимости быстро [переучится](https://doc.rust-lang.org/book/) на Rust\. Обратное очень и очень сомнительно\. Кстати, язык, если что — далеко не единственный и не решающий фактор при выборе нового места работы\. Кроме того, опытный программист на C/C\+\+ без труда вонзается в исходники PostgreSQL или ядра Linux, использует мощные современные инструменты разработки, а также имеет в своем распоряжении множество книг и статей \(скажем, по OpenGL\)\.

Берегите свое время и здоровье, их у вас не так много, как кажется\!