На C++ я программирую уже давно. Лет 25 точно. Не могу сказать, что знаю C++ хорошо, но, наверное, хорошо знаю, как использовать C++ так, чтобы не было мучительно больно. Тем не менее, не могу не признать, что C++ уже весьма стар, сложен и противоречив. Хоть программировать на нем в последнее время становится удобнее, но есть серьезные родовые травмы, которые будут постоянно отравлять жизнь C++ разработчикам. Посему лично я был бы не прочь со временем сменить C++ на что-то более современное, удобное и безопасное.
На мой взгляд, из того, что сейчас существует для разработки из нативных языков без GC, самой реальной и серьезной альтернативой для C++ является язык Rust. Так же мне кажется, что Rust -- это всерьез и надолго. Как много Rust переманит именно C-шников и C++-ников -- это отдельный вопрос, думаю, что изрядное количество Rust-программистов будут составлять те, кто пришел в Rust не из мира C/C++, а из мира безопасных языков с GC и, вероятно, из мира функциональщины.
Сам я пока в Rust всерьез не вложился и причины тому вовсе не какие-то идеологические, а весьма прозаические. Мы с коллегами пытаемся создать бизнес вокруг инструментария для программистов. Что именно начнет приносить нам деньги -- продажа продуктов, продажа коммерческих лицензий, обучение, консалтинг, заказная разработка или что-то другое -- это выяснится со временем. Поскольку у нас большой опыт в C++, то сейчас мы концентрируемся именно на инструментарии для C++. Ибо здесь есть рынок: по некоторым оценкам количество C++ разработчиков в мире насчитывает более 3 миллионов человек, накоплена огромная кодовая база на C++, которая никогда не будет переписана на других языках, да и новые проекты на C++, пусть не часто, но начинают (и выбор C++ в качестве языка реализации нового проекта в современных условиях вполне имеет смысл). Так что в области C++ есть и какой-никакой рынок, и нам есть что на нем предложить.
А вот для Rust-а такого рынка, насколько я могу судить, пока еще нет. Вроде как есть какой-то совсем небольшой спрос на Rust-разработчиков, но вряд ли сейчас найдется много желающих покупать коммерческие библиотеки для Rust-а, или коммерческие лицензии на такие библиотеки. Да и вряд ли кто-то будет готов платить за консультации по Rust-у или за обучение Rust-у разработчиков, переходящих на Rust с других языков программирования. Думаю, что не ошибусь, если скажу, что не смотря на весь хайп вокруг Rust-а, реальное присутствие Rust-а в коммерческой разработке ПО в мире находится где-то на уровне статпогрешности. Посему достаточно емкого рынка инструментария для Rust-а еще просто нет. Может через 3-5 лет такой рынок сложится и достигнет достаточного объема, чтобы на нем можно было бы зарабатывать. Но сейчас это не так.
Ну а поскольку самый дефицитный ресурс -- это время, то я пока не вижу смысла тратить этот ресурс на Rust, если его можно потратить на что-то с более реальными перспективами продажи. И это, наверное, главная причина, почему мы до сих пор не вложились в Rust. Хотя из поля зрения его и не выпускаем.
Но сегодня я бы хотел затронуть еще один аспект. Он касается того, насколько сильно придется перестраивать мозги старому C++нику, вроде меня, при переходе с C++ на Rust. И будет ли такая перестройка того стоить.
Не смотря на то, что знания языка Rust у меня очень поверхностные, в последнее время, когда программирую на C++, иногда задумываюсь о том, а как бы пришлось писать такой код на Rust-е. Такие раздумья приводят к выводу, что переход с C++ на Rust потребует кардинального изменения взглядов на то, как проектировать и как писать код.
Вот, например, из того, над чем я работаю сейчас. Небольшой фрагмент:
template< typename LOCK_TYPE, typename TRACING_BASE > class notify_mbox_t : public abstract_message_box_t , private details::lock_holder_detector<LOCK_HOLDER>::type , private TRACING_BASE { public : template< typename... TRACING_BASE_ARGS > notify_mbox_t( intrusive_ptr_t< time_elapsed_mbox_t > time_mbox, mbox_id_t id, TRACING_BASE_ARGS && ...tracing_args ) : TRACING_BASE( std::forward<TRACING_BASE_ARGS>(tracing_args)... ) , m_time_mbox( std::move(time_mbox) ) , m_id(id) {} ... |
Это шаблонный класс почтового ящика, который параметризуется двумя параметрами. Первый параметр -- это тип объекта, который будет защищать почтовый ящик от многопоточного доступа. В качестве LOCK_TYPE может использоваться std::mutex. Или какой-то вариант spinlock-а. Или null_mutex, если защита от многопоточности не требуется.
Второй параметр определяет, будет ли выполняться трассировка действий над почтовым ящиком. Например, если кто-то отсылает сообщение в почтовый ящик, то будет ли эта операция залогирована или нет.
Например, если в программе создается почтовый ящик, который должен быть защищен std::mutex-ом и должен логировать операции отсылки сообщений, то такой шаблон должен быть раскрыт во что-то вроде:
class notify_mbox_t<std::mutex, tracing_enabled> : public abstract_message_box_t { std::mutex m_lock; msg_tracing::tracer_t & m_tracer; template< typename LAMBDA > auto lock_and_perform( LAMBDA && lambda ) { std::lock_guard< std::mutex > lock{ m_lock }; return lambda(); } public : notify_mbox_t( intrusive_ptr_t< time_elapsed_mbox_t > time_mbox, mbox_id_t id, msg_tracing::tracer_t & tracer ) : m_tracer( tracer ) , m_time_mbox( std::move(time_mbox) ) , m_id(id) {} ... }; |
Т.е. здесь будет и std::mutex, и вспомогательный метод для блокировки объекта на время выполнения какой-то операции, и трассировщик операций над почтовым ящиком.
А вот если нужен почтовый ящик без защиты от многопоточности и без надобности в трассировке, должно получиться что-то вроде:
class notify_mbox_t<null_mutex, tracing_disabled> : public abstract_message_box_t { template< typename LAMBDA > auto lock_and_perform( LAMBDA && lambda ) { return lambda(); } public : notify_mbox_t( intrusive_ptr_t< time_elapsed_mbox_t > time_mbox, mbox_id_t id ) : m_time_mbox( std::move(time_mbox) ) , m_id(id) {} ... }; |
Т.е. в объекте не должно быть вообще ничего, чем объект не пользуется.
Так вот, не смотря на все свои родовые травмы и всю свою сложность, C++ предоставляет мне возможность выразить в коде то, что мне нужно. Поскольку я знаю, как это сделать и как не набить себе шишек, то я думаю, что это делается не так уж и сложно. Не могу судить, насколько просто понимать такой C++ный код менее опытным C++никам. Надеюсь, однако, что не очень сложно. Но разговор не об этом.
Разговор о том, что при переходе с C++ на Rust я не смогу переиспользовать свой C++ный опыт и свои C++ные привычки в Rust-е. Ну вот не получится в Rust-е создать такой же шаблонный класс почтового ящика. Как в Rust-е сделать шаблонную структуру, состав которой будет определяется в зависимости от типов параметров шаблона... И как реализовывать передачу разного набора параметров при конструировании экземпляра такой шаблонной структуры... С ходу не придумывается. Вероятно, без помощи Rust-овой макросовой магии никак.
На самом деле, меня не так уж интересует вопрос о том, можно ли в Rust-е сделать что-то похожее и как это будет выглядеть. Важнее другое: очевидно, что при всех своих проблемах C++ является весьма выразительным языком, с которым понятно, как жить и как его использовать. Да, в нем легко отстрелить себе ногу. Но, с другой стороны, на C++ ты можешь сходу делать сложные решения, тогда как на Rust-е придется учиться решать аналогичные задачи заново. Причем, есть подозрение, с использованием менее выразительных языковых средств. Т.е. в Rust-е ты можешь получить более безопасное решение, но приложив для этого больше усилий.
Я бы добавил сюда еще и то, что Rust-овый код выглядит перегруженным. Т.е. код компактен и лаконичен, но именно это и усложняет его чтение, т.к. в одной строке содержится намного больше смыслов, чем в C++. Так, встретив что-то вроде let a = f.foo()?.bar().unwrap().baz()?.unwrap_or_else(||b); нужно быть очень внимательным.
В общем, к чему я это все: для старого C++ника переход с C++ на Rust выглядит более чем некомфортным. Слишком уж это разные языки, требующие разных подходов к написанию кода. А вот выигрыш от такого перехода в плане выразительности для меня лично отнюдь не очевиден. С точки зрения языка программирования в Rust-е очень подкупает наличие алгебраических типов данных и паттерн-матчинга, а так же нормальной модульной системы. На этом, наверное, именно языковые преимущества и заканчиваются. Все остальные бонусы от Rust-а лежат в области инфраструктуры: единый компилятор и Cargo -- это лучше зоопарка C++ных компиляторов и CMake в качестве де-факто стандарта.
Так что, не смотря на то, что Rust на данный момент единственная реальная альтернатива C++, меня эта альтернатива пока что не прельщает. Мне бы хотелось чего-то вроде очищенного и более строгого C++. С уже привычным ООП, шаблонами, исключениями, перегрузкой операторов. С позаимствованными из Rust-а лайфтаймами, с нормальными модулями и пакетным менеджером. И в привычном синтаксисе, без экономии на спичках в виде fn, mut и ref.
Остается надеяться, что Страуструп был прав, говоря о том, что внутри C++ есть небольшой, красивый и безопасный язык, который стремиться вырваться наружу. Уже как бы пора :)
Комментариев нет:
Отправить комментарий