LINUX.ORG.RU

Посмотрел я этот ваш Rust

 ,


6

8

Вобщем, дошли руки потыкать палочкой.

Я вот что не пойму - зачем и кому он нужен, ну правда?

Это же новый C++. То есть, чрезмерно переусложненный язык, в котором, как говорил Луговский, разобраться может разве что хорошая зубрилка, а не хороший программист, но при этом не дающий никаких бонусов к продуктивности, и никакими киллер-фичами не обладающий.

Close to metal? Нет, извините, мне когда надо будет close to metal - я пойду сишку возьму. Которая реально, и Close To Metal, и со стабильным ABI, так важным для низкоуровневого программирования, и так далее. А если просто производительности не будет хватать, в том числе там из-за GC, так ведь - что в Java, что в Common Lisp, есть огромное количество возможностей затюнить производительность до нужного уровня, при этом не стреляя себе в ногу.

Продуктивность разработчика? Я сильно в этом сомневаюсь. Потому что вот есть языки программирования, предлагающие наибольшую продуктивность, не ограничивающие пользователя практически никак, и, конечно, вместе с тем, довольно сильно нагружающие межушной нервный узел, довольно нетривиальные для изучения. Как пример, лиспы всевозможные. Но Rust в их число не входит. Там на каждом углу костыли, подпорки, железные двери с замками, и чуть что так обухом по голове можно получить.

Наконец, ну безопасность чтоли, хваленая? Ну, опять нет. Взять тот же unsafe. Если вам нужна прямо таки безопасность-безопасность - берите что-нибудь вроде хаскеля(или какого-нибудь Coq, или что-нибудь подобное, с зависимыми типами, если совсем упоролись), ну или на худой конец, что-нибудь вроде Java, где все безопасно прямо как в дурдоме с мягкими стенами.

Вобщем, не вижу зачем этот язык нужен, нам и C++ хватает, если надо не ехать, а шашечки(т.е. тупо позадротствовать, да).

★★
Ответ на: комментарий от byko3y

При переходе с чего и в какой сфере? Тут должна была быть цитата про забивание болтов и закручивание гвоздей.

С С++. Не думаю, что раст может тягаться в выразительности с более высокоуровневыми (или скорее «менее системными») языками.

Ну ты же упоминал хаскель — неужели тебе не очевидно, что его модель построения логики дает больше гарантий?

Признаюсь - не понял мысль. Да, хаскель даёт больше гарантий, но с растом они всё-таки в разных нишах. Ты к тому что раст не идеал? Глупо будет спорить. Если появится язык с большими гарантиями и лучшей системой типов и сможет перешагнуть грань от «прикольной поделки» (эксперимента) к реально применимому инструменту, то я буду только рад.

Но всё-таки прелесть раста в том, что на нём легко может средний сишник, ну плюсовик уж точно. С хаскелем такой фокус не прокатывает.

Я до сих пор не могу однозначно ответить, что же было в голове у людей, которые решили, что переходить с Си на C++ — это хорошая идея.

Тут мы, пожалуй, тоже разойдёмся во мнениях. В своё время писал на С++ с удовольствием. (:

Да, стало так же плохо, как в шаблонах C++. Представь на секунду, что ты можешь писать на C++ вообще без типов, только тип контейнера при создании указываешь. А теперь задайся вопросом: зачем нужен питон или Go, если у тебя есть такой язык?

Кажется, я опять не понял куда ты клонишь. Это будет всё ещё статически типизированный язык, где просто будет больше всего выводиться? Не знаю насколько удобно с этим работать будет: даже в хаскеле и то аннотации типов пишут на верхнем уровне.

Ну и, опять же, лично я предпочту больше «мусора» в сигнатурах, если он осмысленный. Если столько же информации можно будет записать как-то более удобно (именно удобно, а не просто «короче»), то отлично. А если придётся гадать или заглядывать внутрь функции, то спасибо, но не надо.

DarkEld3r ★★★★★
()
Ответ на: комментарий от X512

В Обероне перехватываются все исключения без разбору и ничего… Имеет значение только упало/не упало.

Всё-таки в языках вроде джавы, C# или С++ исключения можно и нужно перехватывать на разных уровнях и по-разному. Если просто где-то на самом верху ловить и писать в лог, то да - разницы нет.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

В Обероне есть главный цикл обработки событий, если обработчик события сгенерировал исключение, то вызываются зарегистрированные обработчики (например чтобы освободить ресурсы или отключить это событие чтобы бесконечно не падало), управление возвращается в главный цикл и обрабатывается следующее событие.

Например если в результате обработки события отрисовки виджета было исключение, то показывается сообщение об ошибке с названием исключения и трассировкой стека, зарегистрированный обработчик отключает отрисовку виджета (чтобы вечно не падало) и главный цикл переходит к обработке следующего события.

Если это какой-нибудь фоновый сервис без GUI, то событиями главного цикла будут запросы клиента.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 2)
Ответ на: комментарий от DarkEld3r

Потому что с исключениями легко и просто реализуются сценарии вроде «перехватить только конкретное исключение или его наследников», а с паникой - нет

С исключениями легко и просто реализуются сценарии «двойное высвобождение», «доступ по тухлому указателю», «доступ к неинициализированному значению», поскольку нарушается структурированность программы и программа попадает в неопределенное состояние, которое никогда не было отлажено. Как правило, наличие try-catch никак не спасает, если срабатывание исключения не тестировалось так же тщательно, как штатная работа..

byko3y ★★★★
()
Ответ на: комментарий от DarkEld3r

При переходе с чего и в какой сфере? Тут должна была быть цитата про забивание болтов и закручивание гвоздей.

С С++. Не думаю, что раст может тягаться в выразительности с более высокоуровневыми (или скорее «менее системными») языками

Я очень соммневаюсь в существовании разницы по производительности разработки между C++ и Rust. Особенно учитывая существование лямбд и вывода типов в современном C++. Конечно, может быть ты сравниваешь с C++03 — тогда да, вопросов нету.

Если появится язык с большими гарантиями и лучшей системой типов и сможет перешагнуть грань от «прикольной поделки» (эксперимента) к реально применимому инструменту, то я буду только рад

На уровне языков с GC диалекты ML вполне себе шагают по индустрии. Неохотно, да. Но производительности могут быть сравнимы, потому что типизация и компиляция статична, и далеко не все операции делаются в куче — примерно как в Go. А минус Rust-а в том, что он плохо подходит для писания сложной высокоуровневой логики.

Но всё-таки прелесть раста в том, что на нём легко может средний сишник, ну плюсовик уж точно. С хаскелем такой фокус не прокатывает

Я имею в виду модель организации языка, будь то хаскель, ML, Scala, Erlang. Facebook сделал по этой модели либу для писания фронтенда — весь мир теперь в основном ей и пользууется.

Я до сих пор не могу однозначно ответить, что же было в голове у людей, которые решили, что переходить с Си на C++ — это хорошая идея.

Тут мы, пожалуй, тоже разойдёмся во мнениях. В своё время писал на С++ с удовольствием

При чем тут твое удовольствие? Я вот в крестики-нолики когда-то играл с удовольствием, но сейчас меня они не привлекают, и я не считаю их какой-то развитой игрой. К тому же, еще раньше был Си с классами, который был совсем другой.

даже в хаскеле и то аннотации типов пишут на верхнем уровне

В хаскеле пишут аннотации типов ровно настолько, насколько хотят или вынуждены.

Ну и, опять же, лично я предпочту больше «мусора» в сигнатурах, если он осмысленный. Если столько же информации можно будет записать как-то более удобно (именно удобно, а не просто «короче»), то отлично. А если придётся гадать или заглядывать внутрь функции, то спасибо, но не надо

Никто не спорит, что аннотации типов — это хорошо. Плохо, когда выбора нет, и писать приходится.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Facebook сделал по этой модели либу для писания фронтенда — весь мир теперь в основном ей и пользууется.

а что за модель и либа?

Minona ★★☆
()
Ответ на: комментарий от Minona

Facebook сделал по этой модели либу для писания фронтенда — весь мир теперь в основном ей и пользууется.

а что за модель и либа?

Модель — функционально-декларативно-реактивно-херпоймикакая. Важно, что не классическая тьюринговая императивность. Либа — React.js.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

С исключениями легко и просто реализуются сценарии «двойное высвобождение», «доступ по тухлому указателю», «доступ к неинициализированному значению»

Паника, которую можно перехватить (а в Go или Rust именно такая), в этом плане ничем не лучше.

DarkEld3r ★★★★★
()
Ответ на: комментарий от byko3y

Конечно, может быть ты сравниваешь с C++03 — тогда да, вопросов нету.

Нет, конечно. На тот момент, когда ещё активно писал на С++, сталкиваться приходилось, в основном, со стандартами 11 и 14 годов. Напомню, что начал я именно с признания, что объективно это измерить не получится и говорил именно о своих ощущениях.

А минус Rust-а в том, что он плохо подходит для писания сложной высокоуровневой логики.

И почему же?

При чем тут твое удовольствие?

При том же, что и твоё мнение о превосходстве С над С++.

В хаскеле пишут аннотации типов ровно настолько, насколько хотят или вынуждены.

Насколько я знаю, хорошим тоном всё-таки считается указывать, проверять откровенно лень. Сам аргумент «настолько насколько хотят» звучит бессмысленно: «в С++ избегают UB настолько насколько хотят», «в расте злоупотребляют ансейфом настолько насколько хотят». Это мало что говорит о реальной ситуации. У тебя правда есть статистика/опыт по тому как часто в хаскеле пишут анотации типов для публичных функций?

Могу только порадоваться, что в расте вывод типов работает только внутри функций. Интересно было бы посмотреть, если бы язык требовал этого только для публичных функций, но уж лучше так как сейчас, чем полагаться на «настолько насколько хотят» в стороннем коде.

Плохо, когда выбора нет, и писать приходится.

Зато отлично когда приходится читать.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Конечно, может быть ты сравниваешь с C++03 — тогда да, вопросов нету.

Нет, конечно. На тот момент, когда ещё активно писал на С++, сталкиваться приходилось, в основном, со стандартами 11 и 14 годов. Напомню, что начал я именно с признания, что объективно это измерить не получится и говорил именно о своих ощущениях

Ну хз, может, действительно, дело только в твоих личных ощущениях. Потому что объективно разницы немного (именно в плане продуктивности).

А минус Rust-а в том, что он плохо подходит для писания сложной высокоуровневой логики.

И почему же?

Я уже написал — он сильно загаживает верхний уровень абстракций нижним. Иначе как ты объяснишь такое обилие альтернативных высокоуровневых языков. Почему-то никто не рвется писать лендинги на Rust — даже несмотря на то, что ведь можно сделать библиотеку, которая с минимальными трудозатратами даст возмонжость писать лендинги. Однако же, на PHP макака сделает лендинг за два часа, где ты с растом будешь сидеть день.

При том же, что и твоё мнение о превосходстве С над С++

Я никогда такого не писал.

У тебя правда есть статистика/опыт по тому как часто в хаскеле пишут анотации типов для публичных функций?

По крайней мере, в стандартной либе хаскеля публичные функции зааннотированы настолько, насколько нужно для полиморфности этих функций, но не более того. Приватные функции могут быть зааннотированы, могут не быть. То есть, да, для публичных функций аннотации очень желательно делать. Однако, я разве писал про публичные? В среднем по палате большая часть кода таки приватна — конечно, если код не скатывается в безумие абстракций из десяти строчек, к которым пишется своя документация и свое публичное API.

Могу только порадоваться, что в расте вывод типов работает только внутри функций. Интересно было бы посмотреть, если бы язык требовал этого только для публичных функций, но уж лучше так как сейчас, чем полагаться на «настолько насколько хотят» в стороннем коде

Обобщенные функции дает возможность писать полиморфный код без макросов. Например, можно через тип данных-конвеер (он же «контейнер») задать тип для возврата ошибки, который применится ко всем функциям, которые используют результат с этим форматом ошибки. В Rust, насколько мне известно, до сих пор бида-бида с зоопарком обработки ошибок.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Потому что объективно разницы немного (именно в плане продуктивности).

Сомневаюсь, что тут может быть объективность.

Но вообще на нашем проекте (немалая кодовая база на С++) периодически наблюдаю разные забавные случаи. Скажем из недавнего: есть метод, который принимает две ссылки: значение одной копирует, а вторую хранит как есть. Человек не ожидал подвоха и передал временный объект, ну и всё развалилось. Он начал предлагать для наглядности добавлять перегруженный метод вида foo(&&, &) = deleted, чтобы не дать другим так же на грабли наступить. Правда если ссылок будет больше, то вариант не очень. Или использовать reference_wrapper, но это тоже спасёт только от временных объектов, а не от недостаточно живущих. В итоге, кажется, решили, что «это же С++ - надо быть внимательным» и не стали ничего делать.

Про геморрой с подключением зависимостей и не говорю. Разумеется, можно сказать, что в расте хватает своих нюансов и это так. Поэтому и не верю в «объективное сравнение». Добавлю только то, что долгое время я защищал С++ перед коллегами пишущими на расте. Ну ты знаешь как это бывает, когда человек думает, что нашёл инструмент решающий все проблемы - на форуме таких полно. И считал, что если что, то легко перейду обратно на плюсы. Но в какой-то момент понял, что точка невозврата пройдена: если раст помрёт, то я лучше хаскель/скалу попробую освоить. Потому что к «мелким удобствам» привыкаешь.

Я уже написал — он сильно загаживает верхний уровень абстракций нижним.

Разве кто-то предлагает заменять растом все языки? Если сборка мусора устраивает, то надо этим пользоваться - такой (нормальный) язык сразу даёт огромный бонус к выразительности. Ну а если мы всё-таки сравниваем с С++, то я не верю, что есть принципиальная разница в «загаживании абстракций».

Я никогда такого не писал.

Вероятно, я тебя не так понял. Но что тогда должно значить «не могу однозначно ответить, что же было в голове у людей, которые решили, что переходить с Си на C++ — это хорошая идея»? Что у этих языков разные ниши или что-то другое?

Обобщенные функции дает возможность писать полиморфный код без макросов.

Тут согласен. Хотя по моему опыту это удобно когда пишешь небольшой шаблон прям возле места, где он использоваться будет. Но нередко он разростается, появляется желание переиспользовать - шаблон переезжает в «утилсы» и вот тут аннотации становятся уже полезны, хотя код по прежнему «приватный».

В Rust, насколько мне известно, до сих пор бида-бида с зоопарком обработки ошибок.

Да, вроде, всё устаканилось более-менее. Хотя я сам на 100% не уверен, что это финальная итерация.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Но в какой-то момент понял, что точка невозврата пройдена: если раст помрёт, то я лучше хаскель/скалу попробую освоить. Потому что к «мелким удобствам» привыкаешь

Меня это не удивляет — меня удивляет, что ты «долгое время я защищал С++ перед коллегами пишущими на расте». Объективно, C++ неудобен, но все эти неудобства можно «перетерпеть». Тем более, что лямбды в крестах есть, модули и датаклассы скоро завезут. Разница между растом и крестами примерно как между мерседесом S-класса и жигой с движком от волги — да, на работу можно одинаково успешно доехать на том и на другом, но на мерсе ездить тупо намного приятнее и безопаснее. Если фирма выдала жигуль — будешь ездить на нем.

Собственно, меня эта адово перегруженная гора костылей, которую из себя представляет C++, и отпугнула когда-то от языка. Однако, ты ведь привык — я по прежнему не понимаю, откуда у тебя тогда берется разница в продуктивности. Из-за подключения зависимостей? Из-за проверок видимости объектов? У меня есть большие сомнения по поводу всемогущести этого инструмента.

Вероятно, я тебя не так понял. Но что тогда должно значить «не могу однозначно ответить, что же было в голове у людей, которые решили, что переходить с Си на C++ — это хорошая идея»? Что у этих языков разные ниши или что-то другое?

Нет, язык Си был еще хуже для написания крупных систем, чем C++, но C++ сам по себе ужасен, и не в последнюю очередь потому, что унаследовал кучу плохих решений от Си. Лично мне приятнее читать и писать код на Си, потому что он прост и ясен, и если я выстрелил себе в ногу, то это сделал я, а не восьмипалый компилятор, который заоптимизировал какой-то move после инлайна, и не безумные библиотеки, вроде Boost.

Сильно позже, когда я уже испортил себе карьеру программиста, я узнал, что я не один такой, и что на C++ в сишном стиле пишет немало людей, и boost не любят как я, и за наследование высмеивают.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

С исключениями легко и просто реализуются сценарии «двойное высвобождение», «доступ по тухлому указателю», «доступ к неинициализированному значению»

Только у говнокодеров, которые не знают про RAII.

поскольку нарушается структурированность программы и программа попадает в неопределенное состояние, которое никогда не было отлажено

Как говорят оберонщики «отлаживать от слова лажа».

Как правило, наличие try-catch никак не спасает, если срабатывание исключения не тестировалось так же тщательно, как штатная работа..

catch (...) {} от всего спасёт.

X512 ★★★★★
()
Ответ на: комментарий от X512

Только у говнокодеров, которые не знают про RAII

И что ты собрался делать при исключении в конструкторе?

catch (...) {} от всего спасёт.

Спасет, только программа работать при этом не будет — а тогда какая разница?

byko3y ★★★★
()
Ответ на: комментарий от byko3y

И что ты собрался делать при исключении в конструкторе?

Тоже что и обычно.

Спасет, только программа работать при этом не будет — а тогда какая разница?

В Обероне всё прекрасно работает. Если текущий обработчик события в главном цикле упал не важно по какой причине (исключение, NULL dereference, переполнение стека и т.д.), то будет выполняться следующий обработчик. Для GUI при падении можно отметить виджет, метод которого вызвал падение, в веб сервере можно показать страницу с ошибкой. Падать всему процессу не нужно.

X512 ★★★★★
()
Ответ на: комментарий от X512

И что ты собрался делать при исключении в конструкторе?

Тоже что и обычно

Ну что? Когда я писал на дельфях, то самый большой геморрой был как раз из-за того, что хрен поймешь, когда ресурс выделился до конца, а когда — уже освободился, чтобы правильно поставить это самое try-catch. Со временем я пришел к конструкциями «инициализация пустыми значениями — try — инициализация — использование — finally — высвобождение».

Если текущий обработчик события в главном цикле упал не важно по какой причине (исключение, NULL dereference, переполнение стека и т.д.), то будет выполняться следующий обработчик

Он упал по причине некорректного состояни объектов приложения. И упадет еще раз, и еще раз — бесконечное число раз, потому что состояние обработчик починить не может, потому что исправление не было запрограммировано и оттестировано.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Когда я писал на дельфях

А вот и причина всех проблем. Недоклон C++ с синтаксисом Паскаля. В C++ таких проблем нет. И в Обероне тоже (там есть GC и обработчики финализации и чистки после исключений).

Он упал по причине некорректного состояни объектов приложения.

Сбросить или пересоздать объект. Можно сделать при обработки следующего события.

И упадет еще раз, и еще раз — бесконечное число раз, потому что состояние обработчик починить не может, потому что исправление не было запрограммировано и оттестировано.

Значит архитектура была задумана изначально кривая.

X512 ★★★★★
()
Ответ на: комментарий от X512

А вот и причина всех проблем. Недоклон C++ с синтаксисом Паскаля. В C++ таких проблем нет. И в Обероне тоже (там есть GC и обработчики финализации и чистки после исключений)

В «недоклоне C++» нет никаких проблем с исключениями при создании простых объектов — проблема возникает, когда объект непростой, логика его создания непростая.

Он упал по причине некорректного состояни объектов приложения.

Сбросить или пересоздать объект

Какой из? Ведь всё приложение не работает над единственным объектом?

И упадет еще раз, и еще раз — бесконечное число раз, потому что состояние обработчик починить не может, потому что исправление не было запрограммировано и оттестировано.

Значит архитектура была задумана изначально кривая

Да, я пишу про фундаментальную ненадежность производных Тьюринг-машины.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

проблема возникает, когда объект непростой, логика его создания непростая.

А в C++ и с непростыми объектами проблем не возникает.

Ведь всё приложение не работает над единственным объектом?

В нормальной архитектуре применяется декомпозиция и максимальная автономность объектов. Сбросить например тот, который вызвал исключение. Или перед вызовом проверять флаг корректности и сбрасывать если некорректный.

Да, я пишу про фундаментальную ненадежность производных Тьюринг-машины.

Машина Тьюринга тут не причём, программы пишут на языке высокого уровня с высокоуровневыми абстракциями, а не на ленте Машины Тьюринга.

X512 ★★★★★
()
Ответ на: комментарий от byko3y

проблема возникает, когда объект непростой, логика его создания непростая.

Пример на C++:

#include <stdio.h>
#include <memory>

class Object
{
public:
	int fId;
	Object(int id): fId(id) {printf("+Object(%d)\n", fId); if (fId == 2) throw this;}
	~Object() {printf("-Object(%d)\n", fId);}
};

class CompObject
{
public:
	std::unique_ptr<Object> fObj1, fObj2, fObj3;
	
	CompObject()
	{
		printf("+CompObject\n");
		fObj1.reset(new Object(1));
		fObj2.reset(new Object(2));
		fObj3.reset(new Object(3));
	}
	
	~CompObject()
	{
		printf("-CompObject\n");
	}
};

int main()
{
	try {
		CompObject co;
	} catch(...) {
		printf("catch\n");
	}
	return 0;
}

Результат:

+CompObject
+Object(1)
+Object(2)
-Object(1)
catch
X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от byko3y

Однако, ты ведь привык — я по прежнему не понимаю, откуда у тебя тогда берется разница в продуктивности.

Представь, что для того чтобы включить свет тебе надо попрыгать на левой ноге, потом правым коленом дотянуться до выключателя и если ошибся, то бьёт током. Через какое-то время привыкнешь, но если потом станет доступен обычный способ, то возвращаться к старому не захочешь. Даже если привык до автоматизма.

Так и у меня. Поначалу, конечно, постоянно казалось, что в С++ можно гибче извернуться, а потом пришло понимание, что я лучше буду с бороу чекером «бороться», чем писать на плюсах. Сравниваю я, естественно, не с моментом когда только-только начал на раст осваивать.

У меня есть большие сомнения по поводу всемогущести этого инструмента.

Звучит как «у меня есть большие сомнения по поводу всемогущести RAII» (или шаблонов или объектов и т.д.). Да, есть свои ограничения и особенности, но в целом жить проще.

откуда у тебя тогда берется разница в продуктивности

Из всех этих мелочей: бороу чекер, явный ансейф, карго, паттерн матчинг, семантика перемещения о которой даже не надо задумываться, (почти) всё expression, форматирование кода из коробки вместо необходимости спорить о том как настроить clang-format (а до его появления ещё веселее было), макросы и т.д. Последние ты недолюбливаешь, но насколько проще и приятнее написать #[derive(Debug, Serialize, Deserialize)], чем то, что приходится делать в С++.

DarkEld3r ★★★★★
()
Ответ на: комментарий от X512

fObj1.reset(new Object(1));
fObj2.reset(new Object(2));
fObj3.reset(new Object(3));

Ты сейчас привел пример чистого-чистейшего RAII, без каких-либо дополнительных операций и условий, и без хитроумных требований нестандартного порядка деструкции, отличающегося от FILO (стэка). В таком виде с исключением сможет справиться и Delphi, хоть и другим механизмом — все подобъекты являются ссылками на контейнеры и высвободятся непустые указатели.

Проблема в том, что для корректного оформления высвобождения ресурсов в таком стиле нужно на каждый чих и пук оформлять отдельный класс, который опишет отмену эффекта одной строки кода другой строкой кода. А если нужно нестандратное высвобождение, то это еще пару классов. И некоторые люди именно в таком виде пишут свои приложения. Проще ли это, чем писать и отлаживать обработку исключений явно? Не знаю. По мне, так проще написать эти две строчки выделения и высвобождения рядом — примерно как в defer Go или attribute cleanup у Go/Clang.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Проблема в том, что для корректного оформления высвобождения ресурсов в таком стиле нужно на каждый чих и пук оформлять отдельный класс, который опишет отмену эффекта одной строки кода другой строкой кода.

Не так уж и страшно. И можно сделать универсальный класс, который будет вызывать лямбды в деструкторе, если так хочется экономить строчки кода.

Проще ли это, чем писать и отлаживать обработку исключений явно? Не знаю.

Конечно проще, потому что это гарантирует exception-safety в любом месте в отличии от ручного освобождения ресурсов. Восход солнца вручную всегда сопровождается проблемами.

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 3)
Ответ на: комментарий от DarkEld3r

Так и у меня. Поначалу, конечно, постоянно казалось, что в С++ можно гибче извернуться, а потом пришло понимание, что я лучше буду с бороу чекером «бороться», чем писать на плюсах. Сравниваю я, естественно, не с моментом когда только-только начал на раст осваивать

Ну в данном случае я не вижу повышения производительности разработки. Комфорт, безопасность — да.

У меня есть большие сомнения по поводу всемогущести этого инструмента.

Звучит как «у меня есть большие сомнения по поводу всемогущести RAII» (или шаблонов или объектов и т.д.). Да, есть свои ограничения и особенности, но в целом жить проще
бороу чекер, явный ансейф

Поясню: для самых простых случаев проверщик заимствования не нужен, потом возникают чуть более сложные случаи с вызовами между функциями со временными объектами, когда проверщик заимствования уже нужен и полезен, но потом очень быстро возникает ситуация, когда объект в половине случаев уходит в глобальную кучу с очевидным нарушением заимствования — это те самые случаи. когда в высокоуровневых языках начинает работать сборщик мусора во время выполенния для гарантирования корректности работы с памятью, а вот проверщик заимстования тут не помогает.

Ну и да, я тоже не верю во всемогущесть RAII, потому что я уже достаточно покололся им.

паттерн матчинг
всё expression

Приятные плюшки из функционального программирования, которые не выглядят настолько мощно в императивщине. Ну или ты собрался писать на Rust в функциональном стиле, с персистентными структурами данных, с передачей только по значению — тогда тебе эти паттерн-матчинг и «всё выражение» сильно помогут.

карго

Наверное, лучше было упомянуть модули, потому что сам по себе cargo хорош ровно до тех пор, пока безальтернативен.

семантика перемещения о которой даже не надо задумываться, (почти)

Вот тут ничего не смогу оценить.

форматирование кода из коробки вместо необходимости спорить о том как настроить clang-format

Это вообще фактор? По-моему про форматирование кода спорят только те, кому больше заняться нечем.

макросы и т.д. Последние ты недолюбливаешь, но насколько проще и приятнее написать #[derive(Debug, Serialize, Deserialize)], чем то, что приходится делать в С++

Тебя я и искал. Я работал много лет с БД и сеткой, и я никак не мог понять: кому нужна эта сериализация-десериализация? Отладочное логирование — да, принимается, макросы для отладки и нужны. Но куда-то передавать и хранить сериализованное значение? Это же значит, что твой тип данных становится API и его нельзя менять, при этом сама сериализация-десериализация непрозрачна, ты не делаешь ее с расчетом не будущее, получается просто «что-то». Если же ты проектируешь сериализацию-десериализацию, то часто получается, что структура сериализованного представления разительным образом отличается от родных объектов.

byko3y ★★★★
()
Ответ на: комментарий от X512

И можно сделать универсальный класс, который будет вызывать лямбды в деструкторе, если так хочется экономить строчки кода

Что по прежнему не решит проблемы нестандартного высвобождения, вызваного зависимостями между объектами.

Проще ли это, чем писать и отлаживать обработку исключений явно? Не знаю

Конечно проще, потому что это гарантирует exception-safety в любом месте в отличии от ручного освобождения ресурсов

Без проверки заимствования можно напороться на проблему. Как правило, у тебя локальные объекты представляют собой что-то взаимосвязанное, а не просто красивую древовидная структура независимых сущностей. Да оно еще и не внешние объекты может ссылаться. По этой причине, как ни странно, Rust имеет значительное преимущество в этом конкретном случае, и может гарантировать чуть больше exception-safety.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

По мне, так проще написать эти две строчки выделения и высвобождения рядом — примерно как в defer Go

в go пишут defer file.Close() и не парятся, что это Close может вернуть ошибку. Ты же вроде только что протестовал против такого подхода?)

Lrrr ★★★★★
()
Ответ на: комментарий от DarkEld3r

Ну нет, не зря в С++ пишут огромные статьи про «exception safety».

А на Rust-е этому вопросу внимания не уделяют?

Если нет, то почему? Ведь не суть, брошено ли исключение или сделан преждевременный return, вопрос-то в сохранении корректного инварианта.

Складывается ощущение (по рассказам на форумах), что в Rust-е народ просто не парится, раз паника, то паника и ниипет, восстановление не предусмотрено в принципе.

eao197 ★★★★★
()
Ответ на: комментарий от Lrrr

в go пишут defer file.Close() и не парятся, что это Close может вернуть ошибку. Ты же вроде только что протестовал против такого подхода?

Я протестовал против того, что «просто лови исключения и всё будет работать». Ты тоже приводишь пример простой логики — а что делать, если файл нужно закрыть после того, как будут высвобождены зависящие от него объекты? В Go для этого применяется явная обработка ошибок, и, на мой взгляд, это единственный способ справиться с проблемой.

byko3y ★★★★
()
Ответ на: комментарий от eao197

в Rust-е народ просто не парится, раз паника, то паника и ниипет, восстановление не предусмотрено в принципе.

Паники не для control flow. Они для индикации багов: произошло что-то непредусмотренное программистом. Какие тут инварианты могут быть? Программист такой случай не предусмотрел или сделал что-то неправильно. Единственный способ восстановления - это как-то сообщить программисту, чтобы исправил (а пока не исправил убить процесс или поток).

Впрочем, panic safety надо заморачиваться, но только в unsafe коде.

red75prim ★★★
()
Ответ на: комментарий от red75prim

Паники не для control flow.

Зуб даете? А то как-то ваши слова расходятся с тем, что в Rust-е есть возможность перехватить панику.

Или правильные пацаны всегда компилируются в режиме panic == abort?

Единственный способ восстановления - это как-то сообщить программисту, чтобы исправил (а пока не исправил убить процесс или поток).

С убийством процесса все понятно. Про убийство отдельного треда интереснее. Что, если паника была брошена в процессе модификации разделяемых данных (естественно, с корректным захватом мутексов и вот этого всего)? На инварианты этих разделяемых данных положим большой и толстый?

Впрочем, panic safety надо заморачиваться, но только в unsafe коде.

Ну или растоманам нужно начать писать не только сервер-сайд, где можно килять процессы с расчетом на автоматические рестарты.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Зуб даете?

Серьёзно? Нужно объяснить почему иногда процесс должен работать несмотря на наличие багов? А значит нужен и механизм для обеспечения такой возможности. Ну и в асинках возможность контроля паник нужна, чтобы экзекутор не помирал при панике в одной из задач.

Что, если паника была брошена в процессе модификации разделяемых данных (естественно, с корректным захватом мутексов и вот этого всего)?

Мутекс (из стандартной библиотеки) помечается как poisoned. Что с ним делать решает программист. Можно попытаться извлечь данные. Можно запаниковать и убить все потоки, использующие эти данные.

где можно килять процессы с расчетом на автоматические рестарты

Для обработки ошибок есть другие механизмы.

red75prim ★★★
()
Последнее исправление: red75prim (всего исправлений: 1)
Ответ на: комментарий от red75prim

Серьёзно?

Я вот не пойму, это вы несерьезно или у вас шиза, иначе как-то не складывается одно с другим:

Паники не для control flow.

Нужно объяснить почему иногда процесс должен работать несмотря на наличие багов? А значит нужен и механизм для обеспечения такой возможности.

Вы, блин, определитесь.

Для обработки ошибок есть другие механизмы.

Поскольку вы явно либо не прочитали то, что я писал, либо не в куриваете, то поясню еще раз.

exception safety в C++ одинаково важна и одинаково хорошо работает как при наличии исключений, так и при преждевременных return-ах. Обеспечивать инварианты нужно и так, и так. Поэтому в C++ понятию exception safety уделяют такое пристальное внимание.

В Rust-е, как и в C++, есть преждевременные return-ы. При которых обеспечивать инварианты нужно точно так же, как и в C++.

Но при этом в Rust-е есть еще и паники. Которые типа не исключения, но типа могут использоваться и как исключения. А раз так, то и проблема exception safety должна была бы быть актуальна и в Rust-е.

Но растоманы этому аспекту внимания уделяют гораздо меньше, чем C++ники.

У меня есть несколько версий почему так происходит. Тут и факт того, что о Rust-е гораздо больше трындят на форумах, чем реально используют в продакшене. И факт того, что когда Rust используют, то это, в основном либо сервер-сайд с возможностью рестартов, либо утилиты типа ripgrep, от краха которых никто не пострадает.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Вы, блин, определитесь.

Ладно, я тоже грешу тем, что сомневаюсь во вменяемости собеседника, когда не понимаю, что происходит в его голове. Так что распишу по буквам.

Паники не предназначены для организации control flow в процессе выполнения программы предусмотренным программистом образом. Для этого есть Result’ы. Паники предназначены для индикации непредусмотренных программистом обстоятельств: выход индекса за границу массива, в том случае если он не должен выходить ни при каких условиях, отсутствие файла, который обязательно должен лежать по такому-то пути (например, потому что мы только что его туда записали) и т.п.

Но паники при возникновении, естественно, создают свой control flow (если программа не собрана с panic=«abort»), который иногда нужно контролировать.

У меня есть несколько версий почему так происходит.

Я уже свою версию озвучил: паника = баг (или непредусмотренная ситуация), доступное потоку состояние недоверенно, полагаться на инварианты в забагованном коде бессмысленно (паники как раз и должны возникать в том числе при нарушении инвариантов).

red75prim ★★★
()
Последнее исправление: red75prim (всего исправлений: 1)
Ответ на: комментарий от byko3y

Ну в данном случае я не вижу повышения производительности разработки. Комфорт, безопасность — да.

По моему эти вещи напрямую связаны.

Если прибегать к аналогиям, то представь, что у тебя на клавиатуре кнопки заедают. Так-то не сложно нажать ещё раз, если с первого раза не сработала, ну и иногда поправить залипшую. Но набирать текст будет намного приятнее (и быстрее) без этих «мелочей». Я уж не говорю о том, что если приходится ещё задумываться о том, что печатаешь, то неудобства могут сбивать с мысли.

С безопасностью похожая история: мы ведь говорим об общей продуктивности, а не просто «написать какой-то код, который скомпилировался», а дальше не важно.

Поясню: для самых простых случаев проверщик заимствования не нужен

Тут можно поспорить. Я бы сказал, что ситуации когда это полезно возникают намного чаще, чем кажется.

но потом очень быстро возникает ситуация, когда объект в половине случаев уходит в глобальную кучу с очевидным нарушением заимствования

Откуда тут возникает нарушение правил заимствования?

Причём тут сборка мусора тоже не понял. Сборщик мусора очень полезен когда не очевидно кто кем владеет или хотя бы когда возникает множество шаред поинтеров. Но и в С++ и в расте этим стараются не злоупотреблять.

Ну и да, я тоже не верю во всемогущесть RAII, потому что я уже достаточно покололся им.

Так и нет «всегогущести», но инструмент это очень полезный. Не решает все проблемы на свете, но без него было бы намного печальнее. Именно этого мне, в первую очередь, и не хватает в С. Аналогично с бороу чекером: после того как распробовал этот инструмент, не хочу больше писать на «низкоуровневом» языке, где нет такой поддержки.

Приятные плюшки из функционального программирования, которые не выглядят настолько мощно в императивщине. Ну или ты собрался писать на Rust в функциональном стиле, с персистентными структурами данных, с передачей только по значению — тогда тебе эти паттерн-матчинг и «всё выражение» сильно помогут.

Мне кажется, что ты сейчас какими-то лозунгами говоришь. Между «функциональным стилем как в хаскеле» и императивщиной есть достаточно большая промежуточная ниша и там раст себя неплохо чувствует. Ну и повторюсь: многие перечисленные штуки - это, конечно, мелочи. Но суммарно они создают ощутимый эффект.

Наверное, лучше было упомянуть модули, потому что сам по себе cargo хорош ровно до тех пор, пока безальтернативен.

Не уверен, что понимаю, что это значит. Речь о том, что если вдруг появится альтернатива, то будет зоопарк? Ну… возможно. Вот только ничто этого не предвещает - если инструмент удобен, то нет смысла придумывать замену. Тем более, что карго достаточно неплохо расширяется. В качестве примера: изначально у карго была недостаточная хорошая поддержка кросскомпиляции. В итоге родился такой инструмент как xargo. Но в итоге карго допилили и всем стало проще жить.

Напомню, что карго - это не только «пакетный менеджер» и «система сборки» - это ещё общий интерфейс для кучи инструментов: форматирование кода, статический анализ, сборка, тестирование - всё запускается через карго. Наверное, правильнее было бы сказать «инфраструктура», но карго - это значительная её часть.

Это вообще фактор? По-моему про форматирование кода спорят только те, кому больше заняться нечем.

Ещё какой фактор. Даже в расте попадаются проекты, которые успели сформировать немалую кодовую базу до того как rustfmt довели до ума и стабилизировали. Над одним из таких довелось поработать так, что могу сравнить. Намного проще и приятнее запускать форматирование и не париться, что на ревью тебе будут тыкать «а вот тут надо иначе скобки расставить». Причём люди на это тоже своё время тратят: пишут гайдлайны, пишут комментарии на ревью… И да, я из тех, кто считает, что единообразие - это хорошо, но пусть лучше за этим инструмент следит.

В С++, наверное, ещё хуже так как clang-format сравнительно недавно появился. На соседнем проекте .clang-format из примерно ста строчек состоит.

кому нужна эта сериализация-десериализация?

Всем?.. Начиная с того, что если тебе откуда-то приходит JSON, то формат его как правило фиксирован, а работать намного приятнее с нативной для языка структурой, а не JSON напрямую. В обратную сторону это работает точно так же. Аналогично с конфиг файлами: удобнее считать его в структуру, а не в мапу.

Это же значит, что твой тип данных становится API и его нельзя менять

Во первых, есть нюансы. Во вторых, часто это нормально. Скажем, когда я писал блокчейн, но у нас был программный (растовый) интерфейс и интерфейс для веб-клиентов. То есть структуры уже были частью API и то, что они ещё сериализовались в JSON погоды не делало.

часто получается, что структура сериализованного представления разительным образом отличается от родных объектов.

Можно поспоришь насчёт того насколько часто и насколько разительно. Но именно для этого есть атрибуты и, в большинстве случаев, можно обойтись ими: указать что какое-то после не надо сериализовать, какое-то переименовать и т.д. В моей практика этого хватало. Сериализация в расте гибче, чем просто навесить один атрибут. Вот тут можно почитать, если интересно.

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

А на Rust-е этому вопросу внимания не уделяют?

Уделяют, ведь принципиальной разницы нет.

Складывается ощущение (по рассказам на форумах), что в Rust-е народ просто не парится, раз паника, то паника и ниипет, восстановление не предусмотрено в принципе.

На лоре почему-то регулярно пишут о том, что панику нельзя перехватить и обработать. Это, разумеется, не так. И если в коде своего приложения можно не париться (и даже включить panic = "abort"), но библиотечный код так поступать не может (ну или не должен).

Я бы даже сказал, что наоборот добавляется нюансов: библиотечный код должен корректно работать вне зависимости от того какая стратегия обработки паники выбрана в приложении. У С++ в стандарте ведь ничего нет про -fno-exceptions?

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Не только.

«Заморачиваться» не особо подпадает под «it is good to be exception safe».

ведь принципиальной разницы нет.

Есть. Паники, в отличие от исключений, - это не способ обработки предусмотренных программистом ошибочных ситуаций.

red75prim ★★★
()
Последнее исправление: red75prim (всего исправлений: 2)
Ответ на: комментарий от red75prim

Есть. Паники, в отличие от исключений, - это не способ обработки предусмотренных программистом ошибочных ситуаций.

Не уверен, что это принципиальная разница, если мы рассматриваем сам механизм.

Давай зайдём с другой стороны: по твоему нормально, если библиотечный код после перехваченной пользователем паники оказывается в невалидном состоянии? При условии, что паника тоже возникла в пользовательском коде, а не в самом библиотеке.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Не смысла рассматривать механизм сам по-себе, если в разных языках он предназначен для разных задач.

по твоему нормально, если библиотечный код после перехваченной пользователем паники оказывается в невалидном состоянии?

В расте есть разные невалидные состояния. Невалидное состояние вызывающее undefined behavior, которого можно добиться только unsafe блоками (баги компилятора опустим), - недопустимо. Нарушение логических инвариантов, которые не проверяются библиотекой - очень плохо. Нарушение логических инвариантов, которые проверяются библиотекой и вызывают панику - это более-менее (невалидное состояние ненаблюдаемо).

red75prim ★★★
()
Последнее исправление: red75prim (всего исправлений: 1)
Ответ на: комментарий от DarkEld3r

Причём тут сборка мусора тоже не понял. Сборщик мусора очень полезен когда не очевидно кто кем владеет или хотя бы когда возникает множество шаред поинтеров. Но и в С++ и в расте этим стараются не злоупотреблять

Что у вас за ниша такая, где нет проблем с определением владения переменными? Обработка запроса в виде единственного большого разветвленного вызова функций? Даже в банальном GUI это уже не прокатывает, потому что есть сторонние функции, которые могут довольно неожиданно менять разделяемое состояние, и на это нужно оглядываться. Я просто большую часть карьеры писал GUI, и потому для меня самоочевидно, что проверщик заимствований довольно мало проблем решает.

Ну и да, я тоже не верю во всемогущесть RAII, потому что я уже достаточно покололся им.

Так и нет «всегогущести», но инструмент это очень полезный. Не решает все проблемы на свете, но без него было бы намного печальнее. Именно этого мне, в первую очередь, и не хватает в С

Attribute cleanup же. Здесь я бы хотел уточнить, что для того, чтобы RAII было более-менее удобно пользоваться, необходим еще некоторый уровень метапрограммирования и системы типов, которого нет в Си, так что сам по себе ввод RAII в Си не решил всех проблем.

Приятные плюшки из функционального программирования, которые не выглядят настолько мощно в императивщине. Ну или ты собрался писать на Rust в функциональном стиле, с персистентными структурами данных, с передачей только по значению — тогда тебе эти паттерн-матчинг и «всё выражение» сильно помогут

Мне кажется, что ты сейчас какими-то лозунгами говоришь. Между «функциональным стилем как в хаскеле» и императивщиной есть достаточно большая промежуточная ниша и там раст себя неплохо чувствует. Ну и повторюсь: многие перечисленные штуки - это, конечно, мелочи. Но суммарно они создают ощутимый эффект

Я не могу вспомнить ни одной ситуации, когда у меня возникла бы мысль «ох, как бы можно было красиво это переписать на паттерн-матчинге». Но при этом без паттерн матчинга функциональщину писать ох как тяжко, потому что без него приходится создавать временные переменные и кучу условий. В императивщине же вполне себе принято делать

let mut a = 1;
if condition {
    a = 2;
}
и не морочить голову выражениями.

Речь о том, что если вдруг появится альтернатива, то будет зоопарк? Ну… возможно. Вот только ничто этого не предвещает - если инструмент удобен, то нет смысла придумывать замену. Тем более, что карго достаточно неплохо расширяется

Раст только-только развивается. Там ведь внезапно может появиться необходимость подключать и даже компилировать либы на других языках, или использовать либы из пакетов дистра системы.

Напомню, что карго - это не только «пакетный менеджер» и «система сборки» - это ещё общий интерфейс для кучи инструментов: форматирование кода, статический анализ, сборка, тестирование - всё запускается через карго. Наверное, правильнее было бы сказать «инфраструктура», но карго - это значительная её часть

Да, карго — это монстрокобмайн, который выполняет совершенно разнородные задачи. Что само по себе является причиной ожидать альтернатив по мере популяризации раста. Функция компиляции, в какой-то степени, схожа с работой компилятора паскаля, который сам разбирается в зависимостях модулей, в отличие от классических сишных компиляторов, которым нужно скармливать файлы с ложечки.

Даже в расте попадаются проекты, которые успели сформировать немалую кодовую базу до того как rustfmt довели до ума и стабилизировали. Над одним из таких довелось поработать так, что могу сравнить. Намного проще и приятнее запускать форматирование и не париться, что на ревью тебе будут тыкать «а вот тут надо иначе скобки расставить». Причём люди на это тоже своё время тратят: пишут гайдлайны, пишут комментарии на ревью… И да, я из тех, кто считает, что единообразие - это хорошо, но пусть лучше за этим инструмент следит

Я на своем последнем рабстве нагнул команду и выклянчил для себя право форматировать код так, как хочу. У меня позиция по этому поводу однозначная, и я пока что не увидел аргументов, почему я не прав: код пишется и читается людьми, единообразие для этой цели полезно, но иногда отклонение от правил делает код намного более читаемым; в частности, отклонения от правил могут применяться для того, чтобы обратить внимание на какой-то кусок кода. Если у лида форматтер вместо мозга, и если единственное, что он может сделать на ревью — это проверить соответствие формальным критериям, то проект умрет и на нем нечего ловить. Хотя, смотря сколько платят, за солидную компенсацию можно и потерпеть.

Начиная с того, что если тебе откуда-то приходит JSON, то формат его как правило фиксирован, а работать намного приятнее с нативной для языка структурой, а не JSON напрямую. В обратную сторону это работает точно так же. Аналогично с конфиг файлами: удобнее считать его в структуру, а не в мапу

Если проект существует больше пары месяцев, то неизбежно возникает ситуация, когда JSON имеет более одного формата. Что с этим делать?

То есть структуры уже были частью API и то, что они ещё сериализовались в JSON погоды не делало

У вас получилось два подобных API в разных форматах, что ли?

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Я просто большую часть карьеры писал GUI, и потому для меня самоочевидно, что проверщик заимствований довольно мало проблем решает.

С GUI у раста всё печально: есть кучка библиотек разной степени готовности, ну и байндинги к разным фреймворкам. Но я не уверен, что проблема в языке. Новыx фреймворков вообще особо не наблюдается, разве что flutter. И всё чаще встречается электрон вместо нормальныx приложений.

Утверждение про бороу чекер для меня совсем не очевидно.

В императивщине же вполне себе принято делать

Хороший пример, в расте я запишу это вот так:

let a = if condition {
    2
} else {
    1
}

И переменная не будет мутабельной зря, а значит и не надо думать меняется ли она где-то ещё. Если условий несколько, то запись будет даже короче:

let a = match condition {
    x => 2,
    z => 3,
    _ => 1,
}

Мелочи? Да. Удобнее? Тоже да. А если вспомнить, что в расте повсюду Option/Result (или даже Result<Option<T>, E>), то это становится ещё значимее. И весьма вероятно, что условие выше было бы записано как-нибудь вроде let a = opt.unwrap_or(1).

Кстати, для работы с плюсовым std::variant паттерн матчинг был бы весьма удобен.

Там ведь внезапно может появиться необходимость подключать и даже компилировать либы на других языках, или использовать либы из пакетов дистра системы.

Эта необходимость появилась сразу и всё работает. Не напрямую через карго, который как раз не хотят превращать в универсальный комбайн для всех на свете языков, а благодаря тому, что у библиотеки может быть произвольный билд скрипт. Традиционно делается тонкий враппер, который и умеет собирать или подключать готовую библиотеку.

Да, карго — это монстрокобмайн, который выполняет совершенно разнородные задачи. Что само по себе является причиной ожидать альтернатив по мере популяризации раста.

Я бы не назвал эти задачи прям разнородными. Насчёт появления альтернатив: поживём - увидим. Я бы поставил на то, что в течении пяти лет альтернатив не появится.

Кстати, у хаскеля как раз есть два популярных инструмента. По моим ощущениям (человека, который на хаскель смотрит издалека) более новая и совершенная побеждает (или победила). Да, какое-то время будет разброд и шатание из-за разделения экосистемы (или необходимости поддерживать и то и другое). Но если в итоге победит более совершенный инструмент, то никакой беды не вижу. Хуже когда есть несколько плюс-минус одинаковых альтернатив, каждая из которых продолжает развиваться.

но иногда отклонение от правил делает код намного более читаемым

Безусловно. Вопрос в том насколько часто. Опять же, в моей практике это возникает не так часто. Когда clang-format только появился, то он нередко уродовал код, особенно код с макросами. С rustfmt проблем было намного меньше. Возможно в том числе потому, что в расте больше вещей фиксированы, а не отданы на откуп программисту. Подозреваю, что сейчас и clang-format подтянулся.

Кстати, забавно: ты сначала сказал, что о форматировании спорят, только если заняться нечем. А теперь заявляешь, что как раз предпочитаешь «идти против течения» и форматировать по-своему. Мне вот как раз кажется, что если человек начинает бунтовать против стандартного стиля (при условии автоматического и не убогого форматирования), то с ним что-то не так.

в частности, отклонения от правил могут применяться для того, чтобы обратить внимание на какой-то кусок кода

Для этого можно (и нужно) использовать комментарии.

и если единственное, что он может сделать на ревью — это проверить соответствие формальным критериям, то проект умрет и на нем нечего ловить

Про «единственное, что может делать» я не говорил. Хотя, пожалуй, одни из лучших ревьюверов, с которыми мне доводилось работать, были людьми весьма педантичными и могли найти как весьма хитрые и не очевидные нюансы, так и пройтись по именованию или форматированию.

А вообще зачем нужны «формальные критерии» и прочие стайлгайды, если их постоянно нарушать? Если люди не ерундой занимаются, то там как раз будут описаны полезные лучшие практики.

Если проект существует больше пары месяцев, то неизбежно возникает ситуация, когда JSON имеет более одного формата. Что с этим делать?

Зависит от ситуации. Новая версия может получаться по другому эндпоинту или новые поля будут опциональными. Не вижу проблемы.

У вас получилось два подобных API в разных форматах, что ли?

Скорее это были немного разные API, но структуры данных переиспользовались. Скажем, структуры вроде BlockHeader фигурировала как в растовом коде, так и отдавалась в виде JSON.

Кстати, ещё про сериализацию: на соседнем плюсовом проекте вся объектная модель умеет сериализоваться (используется cereal). И во всех соответствующих классах (которых дофига) присутствует код вроде такого:

template <class Archive>
void serialize( Archive & ar )
{
    ar( x, y, z );
}

И да, при добавлении поля надо не забыть добавить его в этот метод.

DarkEld3r ★★★★★
()
Последнее исправление: DarkEld3r (всего исправлений: 1)
Ответ на: комментарий от DarkEld3r

С GUI у раста всё печально: есть кучка библиотек разной степени готовности, ну и байндинги к разным фреймворкам. Но я не уверен, что проблема в языке. Новыx фреймворков вообще особо не наблюдается, разве что flutter. И всё чаще встречается электрон вместо нормальныx приложений.

Утверждение про бороу чекер для меня совсем не очевидно

Да потому что в GUI половина функций просто улетят в unsafe. Вот и весь borrow checker.

И переменная не будет мутабельной зря, а значит и не надо думать меняется ли она где-то ещё

Не спасет от случайного затенения другим объявлением.

А если вспомнить, что в расте повсюду Option/Result (или даже Result<Option<T>, E>), то это становится ещё значимее. И весьма вероятно, что условие выше было бы записано как-нибудь вроде let a = opt.unwrap_or(1).

Кстати, для работы с плюсовым std::variant паттерн матчинг был бы весьма удобен

Вот! Я об этом и пишу. Сначала язык должен возвращать алгебраические и составные типы, а потом уже для раскрытия этих типов нужен паттерн-матчинг. В императивных языках, где не принято так писать, не нужен и паттерн матчинг. Самой писечкой паттерн-матчинг становится, когда применяется для ветвления по полиморфной структуре данных, где каждая ветвь отвечает за конкретизацию типа и раскрытие контейнера, а не просто является switch-case по переменным значениям.

Кстати, забавно: ты сначала сказал, что о форматировании спорят, только если заняться нечем. А теперь заявляешь, что как раз предпочитаешь «идти против течения» и форматировать по-своему. Мне вот как раз кажется, что если человек начинает бунтовать против стандартного стиля (при условии автоматического и не убогого форматирования), то с ним что-то не так

Я же написал, что я придерживаюсь единого принятого в команде стандарта. Где тут описанный тобой бунт? Другое дело — когда этот стиль делают догматом.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Да потому что в GUI половина функций просто улетят в unsafe.

Почему?

Потыкался в имеющиеся «перспективные библиотеки» и не увидел засилья ансейфа в интерфейсе. Можешь и ты посмотреть: https://www.areweguiyet.com/

Не спасет от случайного затенения другим объявлением.

Когда только начинал с языком знакомиться, то меня это тоже очень сильно напрягало. Мол как же так, делают упор на безопасность и защиту от случайных ошибок, а тут такое?! На деле оказалось наоборот весьма удобным когда в коде некая сущность преобразуется несколько раз - не нужно придумывать новое имя каждой переменной. Из-за того, что типы всегда разные, то никогда так в ногу не стрелял. Но да, потенциально может быть источником ошибки.

Сначала язык должен возвращать алгебраические и составные типы

Так ведь для этого не требуется какой-то совершенно новый язык. Так уже можно писать «даже» в С++, а в расте именно в таком стиле и написана большая часть кода. И для этого совсем не обязательное использовать персистентные структуры данных.

Я же написал, что я придерживаюсь единого принятого в команде стандарта. Где тут описанный тобой бунт?

Может я неправильно тебя понял, но не знаю как ещё воспринимать «нагнул команду и выклянчил для себя право форматировать код так, как хочу».

Кстати, про догматы в форматировании кода. Если для локального отключения автоматического форматирования надо повесить на код специальный атрибут, то это повышает порог: дважды подумаешь стоит ли оно того. И на мой взгляд это хорошо.

DarkEld3r ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.