Комментарии 24
Макросы могут привести к увеличению размера машинного кода, ибо легко впасть в копипаст при их использовании, при котором маленькая строка может развернуться в большой блок кода.
Мне кажется это не проблема метапрограммирования как подхода, а следствие недостатка инструментов. Попробовал макросы, понимаешь насколько это круто и начинаешь пытаться впихнуть невпихуемое. Хотя просто недостает механизма полноценной генерации кода.
Не развернете вашу мысль? Мне кажется что есть задачи которые удобнее делать на этапе компиляции, а не выполнения и для таких задач раст предлагает макросы.
Вроде бы Страуструп писал однажды. что макросы — это костыли, которые пытаются сгладить проблемы в языке.
Сравните как устроены макросы в Си и Rust. В первом случае на уровне лексера используется автоподстановка, а в случае второго это полноценная модификация AST (не считая аттрибутных, там как раз в стиле си все).
В результате чего макросы гигиенические и не несут опасности для окружающего кода.
А для любителей более сложной кодогенерации есть возможность в build.rs прикрутить что угодно.
- генерирование избыточного/тривиального кода (boilerplate) вместо его ручного написания.
- расширение языка перед тем, как будет добавлен новый синтаксис, закрытие пробелов в языке.
То есть:
Если вам в языке что-то надо писать «автоматически», повторять много раз похожий код (нарушая принцип не повторяй себя), или вы видите что часто попадается задача для которой надо «расширить» синтаксис языка, этому языку нужны костыли.
Не костылями будут функции профайлинга, вроде file! line!, принтауты которые используются для тестов, любая функция которая нужна при разработке но отпадает при релизе, их можно переложить на нормальное окружение для теста или дебага.
Ещё макросы часто используются для написания разных вариантов кода для разных платтформ, Раст этим страдает?
При этом при наличии полноценного метаязыка, может даже с полной рефлексией действий компилятора фичи Раста не нужны и язык можно разделить на две компоненты —
язык написания кода и язык управления компилятором. Заново изобретаем С++ с улучшенным механизмом макросов.
если разработчик задумает создать свой свой собственный встроенный язык.то он былдокодер
1) Скажите, а на каком таком языке вы пишете, что у вас нет никакой необходимости в бойлерплейт коде?
2) Чем, например, декораторы выгодно отличаются от макросов? А прочая компайл-тайм рефлексия?
Эдак можно любую фичу языка критиковать.
Например, вот этот аргумент можно использовать для того чтобы доказать что подпрограммы — тоже плохо:
Если вам в языке что-то надо писать «автоматически», повторять много раз похожий код (нарушая принцип не повторяй себя), или вы видите что часто попадается задача для которой надо «расширить» синтаксис языка, этому языку нужны костыли.
Меня в макросах больше смущает что они по своей сути полностью прозрачны для всего инструментария: хороший макрос может создать новый уровень абстракции над кодом для человека, но IDE и что подобное никогда не научится им пользоваться нормально. В лучшем случае будет видеть только конечный развернутый код.
Если только специально не захардкодить знание, как для format!
и других стандартных макросов делают, но это не расширяемый подход, конечно.
Есть много причин того, что format! реализован как макрос -6-, я же хочу подчеркнуть то, что он может разделить строку на части во время компиляции, проанализировать ее и проверить, является ли обработка переданных аргументов типо-безопасной
Во многих других языках данные ошибки проявились бы во время выполнения
Интересно, а как Си выполняет подобные проверки во время компиляции в printf() и подобных, если там это не макросы?
Уже нашел в приведенной ссылке на вики:
Some compilers, like the GNU Compiler Collection, will statically check the format strings of printf-like functions and warn about problems (when using the flags -Wall or -Wformat). GCC will also warn about user-defined printf-style functions if the non-standard "format" attribute is applied to the function.
То есть, это чисто фича GCC.
А как с этим в
rust
? Можно язык format!
расширять?В каком смысле раширять? Вообще говоря в Rust есть 2 типажа(класса типов) отвечающих за расширение format!
: Display
и Debug
. Реализация этих трейтов для типа T
как раз отвечает за отображение этого типа с помощью макроса format!
. О таком расширении вы спрашиваете?
В каком смысле раширять?Хороший вопрос. Например в C
register_printf_function
может добавить возмость печать число римскими числами. То есть не только распечатать свой тип (тут вы правы — типажи решают проблему), но и распечатать тип, для которого уже есть «канонический» способ печати как-то по другому. Ну и получить несколько аргументов и их перечатать совместно (хотя это уже блажь: glibc так умеет, но на практике я этого ни разу не использовал).Впрочем 99% потребностей то, что есть у
rust
покрывает, борьба за последний 1% — это уже другая история.Просто забавно, что после всех десятилетий эволюции самый гибкий вывод текста в стандартной библиотеке — у старого доброго C. Rust пока что чуть позади, а про C++ и говорить не стоит: даже поменять местами два аргумента — проблема.
P.S. А, кстати, как с этим у
rust
? Такое как делать умеет:printf(_("Directory \"%s\" does not contain file \"%s\"!"), d, f);
Чтобы при запуске в русской локали оно вывело:
Файл "попа.txt" отсутствует в каталоге "/ай-ай-ай"!
Python и старый добрый C так умеют, а вот C++ — увы…
rust
начнут GUI делать — прикрутят.Но тут, конечно, вступает в противоречие желание статически всё проверять с одной стороны и локализовать без перекомпиляции — с другой.
Как это грамотно разрулить — я сходу не представляю… В C оно сделано ка и всё в C: если грамотно использовать — то работает быстро и хорошо, а если вы где-то ошиблись… ну дык эта, это ж C, шлем и наколенники в комплекте не идут…
Как часть стандартной библиотеки сильно вряд ли. Скорее это будет частью крейта, например, такого как fluent.
>Но тут, конечно, вступает в противоречие желание статически всё проверять с одной стороны и локализовать без перекомпиляции — с другой.
Грамотно написанный макрос будет во время компиляции проверять корректность подтянутых из файлов локализационных строк и переданных аргументов, а так же сообщать об ошибке используя compile_error! с человеко читаемой ошибкой, так что, когда у разработчиков дойдут руки до этого, мы вполне можем получить локализацию на Расте защищённую от выстрелов в ногу, при этом не просто на уровне подстановок, а с «умным» поведением ожидаемым от современных локализационных фреймворков.
Rust: зачем нужны макросы