> А никак.А вот и нет. У раста есть куча вещей - мелких и не очень для этого.
Модули, крейты, пакеты позволяют легко дробить код на логические составляющие и изолировать друг от друга. Писать тесты - юнит тесты, модульные, интеграционные. Причем юнит-тесты пишутся в самом же коде, что очень удобно, т.к. можно без костылей тестировать приватные апи.
DSL для макросов как раз прекрасное решение, намного лучше тупого #define, потому что будет если у тебя в двух файлах два одинаковых #define? А в расте при коллизии имен ты просто указываешь "путь" к нужному.
Или напр. enum. Ну, в си они убогие by design, но еще добавляют проблем - значения должны быть уникальны во всех енамах. И опять привет коллизии. Или уродливые уникальные имена. В плюсах это решили через enum class, а в си, на момент когда о нее пришлось последний раз мараться, нет.
Нормальные "протоколы" в виде трейтов. Мощнейший инструмент, позволяет отделить "намерение" от "реализации", писать нормальные моки для тестов, заодно и избавляющий от кучи ошибок типизации. И необходимости передавать void* в качестве параметра))
> смешивает разные модули (вроде линтера и компилятора)
Конечно это хорошо - оно позволяет получить такой мощный анализ на этапе компиляции.
Даже не понятно, почему думаете что это плохо? Потому что раньше так не делали?
> Но си который старается не делать лишних абстракций это плохо
Плохо не то, что си "старается не делать лишних абстракций", а плохо то, что они не делают их когда это нужно. Вместо написания простых оберток над примитивами и работы с ними, используются чистые примитивы в десятках мест и в одном из них допускается косяк.