Pull to refresh

Comments 543

UFO just landed and posted this here
Да уж, а моя память считает что это рассказ Кинга.

У него есть похожий, "Серая дрянь" называется.


EDIT: Вот так, думаешь "о, точно у Кинга был же такой рассказ", гуглишь 15 минут — а тут уже написали =(

А ещё недавно была новость про Physarum polycephalum

А ещё есть книжка «Мутант 59».

Уже хотел было Дедфуда спрашивать, что за рассказ такой и тут ваш комментарий… Спасибо :)

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

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


С другой стороны, техника прикольная, полезно знать. Или это у меня уже стадия принятия?

Это скорее штука из разряда «о, будет прикольно навернуть такое в пет-проджекте, который всё-равно скиснет через несколько месяцев»
1. Так не работает. Любой пет-проджект в норме замышляется как будущий гугл.
2. Пишется «сложный» код вот почему: если программист познаёт в языке новую (для себя) фичу, выражающую что-то более общо, кратко и т.п., пусть и сложнее понимаемо, всегда будет соблазн её заюзать — «не зря же её придумали; остальные просто не шарят, вот как я до этого момента; подниму уровень своего кода до уровня тех крутых мужиков; да, не все осилят такой код, но проблемы неосиляторов меня уже не волнуют, пусть чувствуют кто в доме папа».
Я говорю как получается, а не как хотелось бы)
Да и такой код ни одного ревью не пройдёт
Всё зависит от решаемых задач и требований… Иногда просто нужно как-нить завернуть, чтобы нормально решить проблему.
У меня коллега тащится с подобных финтов. Постоянно мониторит разные исходники (например на gitHub) и когда удается что ни будь «дикое» найти просто заливается радостью и делится эмоциональным зарядом рассказывая как «это» работает.
Нет, в проекты жуть не затаскивает (ну опять же зависит от личной градации, кому жуть а кому отл. решение).

Мне такое тоже нравится. К сожалению, в последнее время начал замечать, что даже после объяснений не до конца понимаю, почему именно так, а лезть глубже разбираться — фатально не хватает времени. Куда прикольнее становится скрафтить веранду на даче или прикинуться HT1621 и втиснуться в разрыв линии LCD, чего-то попутно меняя в законченном устройстве :)

Это может быть что-то вроде демосцены. Неприминимая на практике разминка для ума.
Когда я или мои коллеги сабимитили баги компиляторов, код в примере всегда ужасен. См например мой репорт github.com/microsoft/TypeScript/issues/25642. Никто в здравом уме никогда так просто не напишет. Подобные конструкции получаются после длительгого упрощения из огромных проектов. Если бы я описал когда реально такое возникает, никто бы не стал читать issue где участвует 1500 классов. Демо пример на то и демо пример что демонструрует проблему минимальным кодом, в этом его задача. Меня всегда прикалывали чуваки которые говорят: не делай так. Я так и не делал специально, оно само по себе из разных частей проекта выросло.

Вы в примере хотите узнать, равно ли круглое съедобному.
Компилятор на это ругается. А вы ругаетесь на компилятор: он глупый, не знает про яблоки.

Тем не менее компилятор шарпов, не зная что есть яблоки разрешает проверить, имея съедобный и круглый объекты, являются ли они ссылками на один и тот же объект. А TS слишком умный и пытается выделываться, в этом и проблема. В моём форке эта диагностика отключена, и никаких проблем нет.
Если быть более корректным, то компилятор не может и не должен знать о существовании яблок. Он должен понимать что множественная реализация интерфейсов — валидная часть языка. И что круглость не может исключать съедобность, а он это предположение делает. Это предположение не верно. Но другой вопрос что очень часто такая проверка возникает как раз при ошибке. Создателями языка выбрано решение что пусть мы будем чаще находить ошибки, как реальные так и ложные, чем все их будем упускать. Это решение мейнтейнеров и в этом нет ничего плохого. В конце концов лишнее приведение к any решает ситуацию. Мне такой подход не подходит потому что я транслирую код из другого языка и там уже есть такие, абсолютно валидные конструкции. К сожалению компилятор не предоставляет опций для поштучного отключения каждой диагностики, но мир опенсорса прекрасен тем что можно пойти, сделать форк компилятора, подправить и жить припеваючи. Что я с удовольствием и сделал, и с тех пор сижу на форке.

Учитывая сколько всяких равенств существует это своершенно корретная вещь.


В том же шарпе чтобы сравнить круглое со съедобным нужно будет использовать ReferenceEquals, а не обычный ==.


В общем, не вижу тут "бага", просто кто-то обожает всякие неявные касты и операторы которые ведут себя совершенно по разному в зависимости от контекста.

Ну так я и говорю что это наоборот плохо. Я был бы рад получить тут ошибку компиляции.

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

Чтобы сравнить указатели стоит вызвать функцию СравниУказатели(a, b), а не a == b. При вызове a == b нужно вызывать метод equal какого-нибудь интерфейса IEquitable, который очевидно в данном случае не реализован. То, что в шарпах оператор == работает не так же, как оператор + например это весьма печально.

Всегда считал и считаю что == именно сравнивает указатели, а не выполняет логическое сравнение, а equals должен быполнять именно логическое сравнение, т.е. например сравнивая прямоугольники мы через == справшиваем, это непосредственно то же самый прямоугольник? А сравнивая через equals мы справиваем, одинаковые ли прямоугольники описаны этими объектами.

Ну к сожалению это не так, и == как раз контекстно-зависимый. Для тех же структур он сравнивает значение, при реализации интерфейса IEquitable он переопределяется для опять же сравнения по значению. Поэтому я и говорю, что для сравнения по ссылке в сишарпе есть отдельная фунцкия, и было бы хорошо если бы в тс сделали так же. А то потом у вас в зависимости от боксинга может сравнение одних и тех же объектов разные результаты давать.

Возможно. Но как минимум в js в который таргетится тайпскрипт и с которым заявляет совместимость, это никогда не так. Тут нет перегрузки операторов совсем и == именно что тупо сравниывает указатели для сложных типов или значения для value типов. А структур нет как таковых.

Но тогда и сравнение с C# некорректное :)

Проблема в том, что существует два вида сравнения:


  1. Два указателя указывают на один объект.
  2. Два указателя указывают на одинаковые объекты.

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

Вообще-то, там ===, а не ==. Т.е. мы хотим узнать, одного ли типа эти объекты, и если да, то одинаковы ли они.
Или вот ещё прекрасное github.com/Microsoft/TypeScript/issues/14833 Это доказательство факта. Грамотным людям понятно какие далеко идущие последствия даёт такой факт. Да, студенты и как вы выразились «половина людей даже не поймёт». Но это не значит что это не нужно обсуждать.

Тьюринг-полная система типов и неразрешимый тайпчекинг — это вообще классика.


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

Да, с парсингом там всё хорошо.

А что не так с этим поведением? В общем-то случае терм некорректный. Чтобы чекнуть — можно всегда делать даункаст.

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


Заголовок спойлера
<scarcasm />
Конечно! А еще настоящий программист никокда не напишет "&mut (x as *mut libc::c_void)", когда надо написать "(&mut x) as *mut _ as *mut *mut libc::c_void".

Ну в моем мире может и написать. Но на счастье в моем мире можно не вылезать из сейф оберточек, с которыми и проблем таких нет.

Хорошо, когда можно не вылезать из оберточек. В моем мире это таки малореально.

Ну мы переписали скала-сервис на хаскель, а затем и на раст, получили ускорение с минуты на обработку тяжелого запроса до двух секунд, и с 2-4гб потребления до 30мб, не вылезая из этого самого сейф кода. Жалко, что в вашем мире это невозможно.




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

Рад за вас :) Кстати, ради любопытства — с минуты до двух секунд — это со скалы на раст или с хаскеля на раст? Каково вообще соотношение было между этими тремя реализациями в этом плане?

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


Ну хаскель тоже ничего, могет, все же получше, чем Java и Scala. Хотя конечно время выполнения хуже растовского в разы, а объем памяти — на порядок. Я-то просто на хаскеле не пишу, и вообще практически не встречаюсь с ПО на нем, было интересно сравнить. Благодарю :)

Интересно ещё объём кода сравнить.


А память… Я как раз написал на хаскеле мелкую штуковину, которая склеивает два файла с временными рядами по десятку гигов каждый и считает всякую статистику по минутным корзинам (за день этих корзин 1440, что поменьше миллиарда точек в исходном файле). Изначально потребление памяти было вообще константным (и я не старался так писать, оно так само получилось), как только пришлось для некоторых статистик сортировать элементы в корзине, оно выросло до этак 30 метров в пике, что, учитывая наличие корзин с миллионом-другим элементов, вполне неплохо.


Было б любопытно переписать на плюсах и посмотреть бейзлайн, но лень.

Емнип около 20к строк на скале, и по 5к строк на хаскелле и расте. Но нужно понимать, что на прототипах еще не весь функционал реализован что был в скале.


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

Такое различие во времени между Java и Scala и между Scala и Rust?
Не может быть. Бред какой-то

Это не бред, а реальная выдержка из прототипирования коммерческой компании, которая делает это не для пиара какого-либо языка, а чтобы снизить издержки. Я с вами поделился результатами, потому что мне они показались интересными. А дальше что делать с этой информацией решайте сами.

Результаты без кода — просто числа, которые ничего не говорят. Разве что порождают спекуляции на тему zero-cost абстракций, что уже специфично для языков. Тоже самое касается работы с памятью. Например, числодробилки на JVM вряд ли стоит делать.

Тут интересно на сам код взглянуть. Как вообще адекватная реализация на Java может уступать в разы реализации на Scala?

Я вроде написал, что реализация на джаве была неадекватная.


А коммерческий код я показать вам не могу, уж извините.

Вспомнил шутку которая не шутка
— По итогу jar нашего продукта занимает всего 1 мб места.
— А почему тогда JVM весит 160+ мб?
— Это всё zero-cost абстракции.

Как раз числодробилки на JVM делать вполне можно, скорость математических операций отличается не сильно. Мы тоже прототипировали, думали выносить ли в С++ либы некую математическую обработку. Получилось что игра не стоит свеч — преимущество в скорости плюсов было невелико (после всех прогревов JVM и JIT — порядка единиц процентов), которое полностью съедалось бы затратами на JNI.

А вы сколько времени на плюсы потратили?


У меня нередко бывало, что несколько дней с профайлером поднимали производительность на 2-3 порядка.

А примеры кода "до-после" остались? Как-то не верится, что без изменения алгоритмов можно выжать 2-3 порядка.

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


Это, например, какой-нибудь random forest, когда начинается всякая акробатика с блочной обработкой данных, максимизацией переиспользования кеша и шины памяти, упаковкой структур, и так далее.

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

В некоторой степени, это и на jvm возможно. Но код будет как на C, или ещё менее читаем.

Без изменения алгоритма, а просто изменение порядка обхода матрицы не влезающей в кэш процессора меняет производительность на 2 порядка. Если добавить оператор at() который проверяет границы массива, и если вдруг (а у меня такое было), процессор всё время ошибается в предсказании перехода, то получается 3й порядок. Если при этом ещё и не хватает TLB то получаем ещё 30-60 % пенальти. т.е. почти 4й порядок. Если добавить математику с денормализованными числами получаем ещё сопоставимую просадку по производительности. Если добавить исключения для передажи обычных, а не исключительных ситуаций получим ещё кратное пенальти. И так можно продолжать до бесконечности.
Там очень тупой алгоритм был — несколько циклов по многомерному массиву и вычисление хитрой формулы для каждого элемента. Деталей уже не помню. Плюсовый код был тупо скопирован с джавы, да там и нечего особо было менять.

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

Не верю. Скала весьма близка к железу, если не обмазываться иммутабельностью. Скорость как у Java — т.е. обычно в ~1-3 раза медленнее, чем C.


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

Ну то есть — идиоматичный Хаскелль побуждает писать код оптимальнее Скалы, а идиоматичный Раст — оптимальнее Хаскелля?

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


Возможно, код на scala содержал много типичных scala-конструкций, которые работают очень медленно, и если не избегать конкретно каждой из них (например, о них не подозревая), код будет тормозить. Раз так в 50 по сравнению с оптимизированным кодом (близком к коду на C), и раз в 100 по сравнению с C.
Не знаю, как обстоят дела в хаскеле, но, возможно, в нём меньше таких неожиданностей. А раст компилируется примерно в те же инструкции, что и аналогичный код на С.


Также согласен с версией, высказанной в комменте ниже.

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

Код был везде идеоматичный. Для случая скалы это zio/monix/местами шейплес. Вполне такая обычная скала.


В хаскелле обычный хаскель код с MTL и расставленными атрибутами а-ля UNPACK.


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

Скорей разработчик переписывая с нуля на новом языке, уже начал смотреть свежим взглядом, но при этом обладал старым опытом.
Так что учел возможные недостатки и недоделки, в итоге заодно получился рефакторинг :)
Полагаю, что если бы один и тот же программист три раза подряд с нуля переписал бы один и тот же функционал на одном и том же ЯП, то результаты бы всё равно с каждым разом становились бы лучше и лучше — каким бы ЯП он не пользовался. Такие сравнения можно проводить «для себя», но аппелировать к ним доказывая, что один ЯП лучше другого — ну такое себе.

Ну там как бэ не просто "ну мы тут переписали", а анализ проводился. Растовый компилятор например практически всё очень хорошо заинлайнил, 5000 строк почти все собрались в один гигантский main, в релизе только ошметки некоторых функций остались.


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


После написания версии на расте, хаскельную тоже дописали, и получили прирост процентов 20, используя знания полученные при написании прототипа (расставление префетчей и likely на условиях), но и все.


Переписывание одной и той же программы на одном и том же языке не даст вам никакого прироста, если вы специально не переписываете ради прироста. У нас в прототипе такой цели не стояло.

Хабр контекстно-зависимый, прям автомат с памятью. Любо-дорого читать!

> как по мне нужно писать «средне»

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

ЗЫ. Писать простой код — это искусство ;)
Профессионалы отличаются тем что пишут простой и скучный код который поймет даже тот кто не знает язык

рано или поздно будет выбор «больше кода на простых конструкциях» (сложнее читать из-за объема) или «меньше кода на конструкциях посложнее» (сложнее читать из-за конструкций). И надо знать золотую середину.
Вообще синтаксис и codestyle проекта должен решать тимлид конечно.
Но писать
return isset($arr['val']) ? $arr['val'] : null
вместо
return $arr['val'] ?? null
на проекте с php7 по моему через чур.

Или ипользуют xml/html для разметки когда есть markdown ( как хабр :) )

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

Это не всегда является значимым критерием при выборе конкретного решения задачи.


Например, люди иногда пишут библиотеки, и оказывается полезным уметь всякие неочевидные финты. Иногда — чтобы сохранить чистый и понятный для новичка в языке API библиотеки, что особенно забавно.

Для библиотек все несколько иначе, согласен.
Но я про конечный продукт, где порой выбор правильной библиотеки делает код намного проще кстати.
O_O
… афигеть… просто нет слов.
Я уже даже перестал нервно смеяться, когда слышу что-нить вроде «С/С++? Да, чо там, простые же языки»…
Не тот язык назвали brainfuck, ох не тот… Хотя всё равно люблю его, но это какой-то капец
Как раз С достаточно прост и определён. А вот из С++ сделали что-то удивительное и душераздирающее.
Доооо, прост, дооо… Есть там свои тёмные места, хотя до плюсов, конечно, далеко…
Ну, там темные места скорее в плане undefined behavior, а тут полное соответствие поведения стандарту.
Именно. Избегай undefined behavior — проблем не будет.
Пользуешься хаками — ССЗБ, не жалуйся.
Ну и объем стандарта немного… Гм… Поменьше.
int x = sizeof(int) > -1;

Чему равен x? Даже на неявных преобразованиях базовых типов можно споткнуться.

Я дилетант, но мне думается, ответа "точно не нулю" на практике достаточно

Для дилетанта да, но вот только это неправильный ответ :)

В сложных случаях провожу «вычислительный эксперимент»:)
// С99
enum E {
  E1 = 1 / ((sizeof(int) > -1) == 0), // компилируется
};

// С11
_Static_assert(sizeof(int) > -1); // IDE красит красным))

Формальное обоснование вроде простое…
UFO just landed and posted this here

Если быть крайне точным, эквивалентно int x = sizeof(int) > SIZE_MAX;

UFO just landed and posted this here

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

Хочется сказать «0», но, похоже, зависит от определения size_t на конкретной платформе и компиляторе — signed или нет.

size_t по стандарту unsigned, а вот его близнец ssize_t — signed.

Ага. Он может быть разного размера, но обязательно unsigned.

Вы неправильно вопрос ставите. Не чему равен, а зачем так.
В моей практике, человек который так написал, не пройдёт код-ревью скорее всего. Потому что зачем так?
После получения от него объяснений зачем и понимания, что не будет ни undefined behavior где-то дальше по коду, ни проблем с переносимостью — можно оставить. С комментариями для потомков, кстати. В данном конкретном месте.

Я вас удивлю, но этот пример переносим и не содержит UB. Значение x всегда будет 0. Выражение uint32_t x = -1 эквивалентно uint32_t x = UINT32_MAX, и этот способ используется для переносимого (!) заполнения всех битов в 1. С другой стороны, обратная операция (знаковое переполнение) уже является UB.


https://github.com/python/cpython/blob/v3.8.0/Modules/socketmodule.c#L2560
https://github.com/python/cpython/blob/v3.8.0/Python/ast_opt.c#L130
https://github.com/postgres/postgres/blob/REL_12_0/src/backend/utils/mmgr/dsa.c#L1234

О. Спрошу слегка не по теме, но близко.

Как сейчас принято CRC считать, раз переполнение == UB?
Только знаковое переполнение является UB, беззнаковое не является и поведение при нем вполне себе определено. Вы же не считаете CRC в знаковых типах, правда?
Спасибо. Про такой нюанс прозевел.
Кстати, может быть знаете, из каких соображений сделали аж UB, а не platform specific?

Хм, заглянул в раздел определений стандарта 13ого года (какой быстрее нагуглился) слегка прифигел от терминологии (undefined behavior и unspecified behavior). Зачем ввели базовые термины, которые в сокращениях совпадают? Не понимаю.

Уже много лет преследует ощущение, что язык пытаются убить, делая его всё более идиотским и переусложненным на ровном месте. А программисты сопротивляются и пишут на нем «вопреки», т.к. привыкли. А небольшой части это даже нравится — ибо можно повыпендриваться ). Но я, скорее всего не прав — просто молодость прошла и учиться стало лениво.
Ну нет, я же написал — «ни undefined behavior где-то дальше по коду, ни проблем с переносимостью», а не про сам пример.
Тем не менее, если эта штука приедет на код ревью, ей понадобится ответ на вопрос «зачем именно так».
Когда учил, то казалось что все более-менее просто в C++. Хотя, про шаблоны я наверное только слышал тогда.
> Да, чо там, простые же языки»…

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


Именно такие цели ставили создатели D.
К сожалению, С++ у него что-то никак убить не получается, он влачит довольно жалкое существование.
Они зачем-то запихали в него GC, это была их основная ошибка.
не основная. сейчас, задним умом, есть недавний опрос-фидбек — что такое хорошо и что плохо, тут это офтоп, так что при интересе найду заново и скину. в личку
Буду очень благодарен, интересно почитать, шош там народ считает основной ошибкой D, если не GC.
И мне в личку, пожалуйста, если нетрудно.
И мне. А лучше в комментариях для всех.
access denied. там специфика не для неокрепших умов
Walter Bright завязал D на «отключаемый» сборщик мусора, мотивируя это уменьшением сложности и отсутствием сколько-либо ощутимой просадки производительности. Только заменить C++ может язык, играющий по тем же правилам. В большинстве случаев, люди используют C++ именно потому, что им необходим полный контроль над ресурсами. А языков с GC сейчас много и они имеют гораздо большую поддержку, чем D.
Лично мне нравятся очень многие вещи в D, но он оставляет впечатление недостаточно проработанного языка.
по-моему, как раз-таки rust и пытается зайти на поляну c++
Из-за своей сложности может и не зайти.

Да что все твердят про сложность раста?) Ей богу, если сравнить с С++, то раст в разы проще синтаксически.

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

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


К тому же, безопасность работы с памятью — это устранение, ну… наверное 95% ошибок при программировании на С++?


К тому же, кроме работы с памятью, Rust предоставляет:


1) Кучу приятного синтаксического сахара вроде match expressions или оператора? для Result и Option (потенциально для любых монад в будущем).


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


3) Пакетный менеджер из коробки


4) Переключение тулчейна без боли и отчаяния


5) Никаких ADL, SFINAE, аргументов по умолчанию, анонимных неймспейсов, static thread_local volatile переменных, неявных целочисленных и не только кастов, strict aliasing-а, noexcept, rvale, lvalue, xvalue, богомерского std::string, ещё более богомерского std::wstring, 20-ти видов инициализации, include-файлов, и бог знает чего ещё...


Нет, я люблю С++. Хотя бы и за то, что после него можно смотреть с улыбкой (и лёгким налетом грусти) на все жалобы в стиле "Python 3.8 стал таким сложным"…


По итогу: раст и надёжнее, из-за того, что убрали почти все способы отстрелить себе ногу. И проще, потому что не нужно знать Make, CMake и ещё с десяток менее популярных или даже самописных систем сборки. В целом, раст наверное даже не сильно медленнее.


Язык этот однозначно ещё не продакшн-рэди, по крайней мере до стандартизации ABI. Но потенциал огромный.

В целом, раст наверное даже не сильно медленнее.

В этот момент я подумал "хехе" и полез искать статью. Потратил минут 10 но нашел. Рекомендую к прочтению. К после прочтения поймете, почему такая реакция :)

Я уже в теме про C# у шарповиков спрашивал, вот спрошу у поклонника раста. Вот есть такое приложение, тот же вопрос, что и шарповикам, только про раст. Предложения «пишите на расте с биндингами» не интересны (потому что зачем тогда вообще раст, если все будет делаться через кучу совершенно лишнего unsafe кода, который просто дергает C/C++ библиотеки), интересует какой-то набор фреймворков/библиотек, которые будут написаны на расте хотя бы процентов на 80. Шарповики как-то плечами пожали, хотя, казалось бы.

Прочитаю инфу по ссылке попозже, пока что отвечу про часть:


потому что зачем тогда вообще раст, если все будет делаться через кучу совершенно лишнего unsafe кода, который просто дергает C/C++ библиотеки

В каком смысле зачем? На расте можно написать safe обертки над этими библиотеками, как миникм5 и уже без страха писать остальную программу. Если поддерживать такую обёртку дорого и сложно, можно тогда и переписать библиотеку, не вижу с этим проблем.


Мне откровенно непонятно стремление выбросить С++ и переписать всё на раст. Тысячи человеколет на С/С++ потрачены зря что ли?). Раст потому и крут, что довольно "легко" интегрируется с Си.

А мне непонятно стремление писать на раст ради того, чтобы писать на раст. "Без страха" — не аргумент, писать на C++ тоже никто не боится, а трата времени и денег на написание бесполезных "safe оберток" вокруг C++ кода бессмысленна — проще сразу писать на C++ и само приложение. Про "лёгкость интеграции" раста уже писал, по-моему, Alexey2005, в одной из тем про раст, это совершенно лишний источник головной боли, который можно оправдать, если C/C++ кода в проекте будет, скажем, 20%, но не подавляющее большинство.

Мне откровенно непонятно стремление выбросить С++ и переписать всё на раст.
Переписывать не надо. Новые проекты лучше не на С++ начинать. В будущем.

если C/C++ кода в проекте будет, скажем, 20%, но не подавляющее большинство
А зачем Расту С++? Одного С вполне достаточно будет.

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

По идее, разработка на идеальном Rust обойдется дешевле, чем на современном С++.
Да, иначе в Rust (условном) не будет смысла.

Остановлюсь на этом пожалуй. А то что-то минуса в карму полетели.
А зачем Расту С++? Одного С вполне достаточно будет.

Ну ОК, никто вас за язык не тянул, достаточно так достаточно. Ознакомьтесь с приложением по моей ссылке выше и ответьте на вопрос, на каком инструментарии вы бы его делали при условии «80% раста, 20% C, никакого C++, так как он не нужен». Все требования к производительности, поддержке платформ и прочему, естественно, сохраняются.
С такими широкими требованиями пока только С++. Что касается перспектив Раста для таких задач, пусть кто-то знающий его ответит.
Как мне представляется, Вы предлагаете не отказываясь от самого плюсового кода, отказаться от его написания.
Я не эксперт в таких вещах, но разве переход на поголовные раст-обёртки не выведут легаси на совершенно непостижимый уровень? Боюсь, что прогресс не остановится на Расте и рано или поздно на рынок таки выйдет язык, который должен будет серьёзно уделать как минимум плюсы (вероятно, и Раст, раз они метят в одну нишу), язык, который все до сих пор именуют «убийцей плюсов». К тому моменту, если переходить на написание Раст-кода над С++ обёрнутого, количество людей, которые захотят переписать легаси из С++ + Раст над ним, будет в разы меньше тех, кто переписал/переписывает Сишное легаси.

Писать десктоп-приложения на расте на текущий момент не на чем, надо брать Qt, как у вас и сделано.


Впрочем, десктоп-приложения в 2019 году мало кому нужны поэтому это не особо серьезная проблема.


"Без страха" — не аргумент, писать на C++ тоже никто не боится,

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


а трата времени и денег на написание бесполезных "safe оберток" вокруг C++ кода бессмысленна

А это другой вопрос. Писать на расте и экспортировать сишное апи действительно неудобно. Но это совсем другая разница, как говорится.

"Потребности в колбасе на сегодня нет", как в старом анекдоте? :) Ну ОК.

Нет, я просто честно говорю про два факта:


  1. для десктоп-разработки на раст сегодня ничего предложить не может
  2. десктоп-разработка на сегодняшний день не очень востребованна, можно по вакансиям глянуть. Тот же сишарп в котором равноценно есть десктоп WPF и в больше чем 90% вакансий ищут именно веб.

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

Ну, рецепт простой, для десктопа в 2019 году нужно брать плюсы, как ни странно :)

Не факт — есть наверное десяток нормальных Си-шных GUI-либ, к которым есть обертки для многих языков — выбирай для удобного себе языка.
Заодно хотелось бы уточнить, т.к. я пока не понял
В отличие от D у раста позиционирование совершенно четкое
А какое оно, можно оф.ссылку?
А поточнее? Я там вижу
From startups to large corporations, from embedded devices to scalable web services, Rust is a great fit
А это нифига не четкое позиционирование…

Везде, где вам нужна производительность С++ с безопасностью высокоуровневых managed-языков, либо bare metal/embedded/...

Ясно. Третьей попытки не надо =) Слово «позиционирование» можно посмотреть в словаре.

То есть язык не может позиционироваться как "общего назначения", я правильно понимаю Вашу мысль?

Уточню вопрос: язык может чётко позиционироваться как "общего назначения" (т.е. "применять с успехом можно везде, если скилла хватит"), или это будет "не чётко"?

для десктопа в 2019 году нужно брать плюсы

Скорее JavaScript и Electron.


А C++ — для игр разве что, и прочего полноэкранного графического софта. И то, во многом потому что Unreal Engine и прочие Unity не так-то быстро переписать на Rust.

Скорее JavaScript и Electron.

Ыыы. У меня прямо сейчас Skype на десктопе полгига RAM отжирает, больше отжирает только Хром, да и то, в нем открыто где-то с десяток вкладок, а отжирает он только в три раза больше. Я еще помню времена, когда Skype жрал на порядки меньше, а умел в разы больше. Electron is Cancer ©.
А ещё при запущенном скайпе юнит тесты (правда чувствительные к свободному объему кэша процессора) у меня на ноутбуке вдвое дольше выполняются. Т.е. не только он сам скайп тупит, но ещё и заставляет кратно тупить другие процессы.
Писать десктоп-приложения на расте на текущий момент не на чем

В будущем подгонят druid

Язык этот однозначно ещё не продакшн-рэди, по крайней мере до стандартизации ABI.

Не то что плюсы.

Ну, скажем стоило бы сказать "до стабилизации ABI". И хоть каких-то гарантий от компилятора)

ну так можно сказать что есть itanium abi и все остальные отщепенцы. (msvc/pgi)
В смысле Rust еще не продакшн-реди, а плюсы уже не продакшн-реди?))))
MooNDeaR Не спорю, что Rust будет мощней (а также проще, легче и надежней). Тем более меня по его новизне и новшествам уже поправили. Но плюсовики — это же как каста со своей психологией (и так почти для любого ЯП). Они станут писать игры на Rust? Десктоп на Rust (кроме браузеров)? А сложное ПО — да, их работодатели заставят.

Да хз, я сам из этой "касты") Мне нравятся плюсы, нравится их просто невероятно мозговыносящие конструкции на шаблонах и макросах. Я испытываю искреннее удовольствие от приведённых в статье конструкций, НО! Это чисто академическое удовольствие, как удовольствие от решённой головоломки.


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

Куча народа пишут игры на Rust. Правда пока нет серьезного движка то ААА на нем мы наверное пока не увидим, но вот все остальное — пожалуйста.

Правда пока нет серьезного движка то ААА на нем мы наверное пока не увидим

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

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

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

Ну, что не надо делать идолов, это да, но с остальным я не согласен:


1) Все остальные языки используют наработки С++, чтобы упростить написание прикладных приложений. Rust же позиционируется как системный ЯП, а потому в сущности предлагает делать всё то же самое, что С++, только "правильно". Остальные попытки победить С++ всегда имели какой-нибудь фатальный недостаток, вроде GC в D или непродуманной системы типов и все такое.


2) Жрать что дают скучненько и вгоняет в депрессию. Плюс, команду можно и дообучить, а кривизну плюсов уже не выправить. В конце концов, мы же о С++ говорим. Если человек осилил плюсы и инфраструктуру вокруг них, то разобраться с Rust не составит труда.

Ну это да, тут спорить трудно. Все хотят победить конкретно плюсы, но конкретно они уже давно монстр, каких свет не видывал, о чём и статья…

А вот «жрать что дают скучненько» — тут важен разумный компромисс между решением конкретных задач и всем вот этим дообучением команды и прочим… Так-то и сайты на плюсах кропать никто не мешает интересненькие со встроенным сервером например, но зачем?
К тому же, безопасность работы с памятью — это устранение, ну… наверное 95% ошибок при программировании на С++?


Вот сильно не уверен. Не скажу за всех, но лично мне очень кажется, что страшилки про «небезопасность работы с памятью в С++» сильно преувеличены. Уже давно придумана масса вполне надёжных техник, которые от этой проблемы избавляют. По крайней мере лично в моём опыте я даже не вспомню, когда в последний раз меня это кусало… Может разве что лет 20 назад, когда только учился, да и то не факт, поскольку переход на С/С++ после ассемблера очень облегчён.

Но вот всякие хитропопые undefined behaviour или х10 ускорение после разборки с data aliasing и т.д. и т.п. — вот это периодически случается и сейчас. Про пример в посте вообще не вспоминаю, — это ж поседеть можно, пока допрёшь что за фигня, если не сталкивался.

Главная беда, С++, имхо абсолютно не работа с памятью (она вполне безопасна и комфортна после некоторого уровня понимания), а адски замороченный стандарт с «кучей условий мелким шрифтом, сносками и звёздочками», что как раз демонстрирует пример этого поста… Но главная беда этой главной беды в том, что она, скорее всего, практически не излечима, просто потому, что заменив сложность на простоту почти всегда неминуемо проиграешь в конечном итоге в эффективности, поскольку вся эта сложность сделана с единственной целью — позволить компилятору выжимать максимум возможной эффективности из кода.
Не скажу за всех, но лично мне очень кажется, что страшилки про «небезопасность работы с памятью в С++» сильно преувеличены

Тоже не скажу за всех, но Segmentation Fault на моей памяти самая частая причина почему падает плюсовое приложение.


Уже давно придумана масса вполне надёжных техник, которые от этой проблемы избавляют.

Никто и не отрицает, что на С++ нельзя писать хорошо. Проблема в том, что устроить состояние гонки в плюсах очень и очень просто. Получить UB на ровном месте из-за нарушения strict aliasing — еще проще. Собственно в этом и разница между С++ и Rust. На С++ сложно писать правильно, нужно следовать куче практик из Core Guidelines. На Rust нужно проделать столько же усилий, но только в обратную сторону — чтобы начать писать неправильно. И все эти места еще явно обозначить как unsafe.


Но вот всякие хитропопые undefined behaviour или х10 ускорение после разборки с data aliasing и т.д. и т.п. — вот это периодически случается.

И всё это решается Rust-ом просто на уровне правил языка. Получить UB сложно, получить нарушение strict aliasing вообще невозможно.


Главная беда, С++, имхо абсолютно не работа с памятью (она вполне безопасна и комфортна после некоторого уровня понимания), а адски замороченный стандарт с «кучей условий мелким шрифтом, сносками и звёздочками», что как раз и демонстрирует пример этого поста…

Вот это как раз-таки не сильно большая проблема. Конструкции из статьи в реальном коде встречаются чуть реже, чем раз в год.


вся эта сложность сделана с единственной целью — позволить компилятору выжимать максимум возможной эффективности из кода.

А вот это можно обозначить за главную проблему современного стандарта С++ :) На нём приходится писать так, чтобы удобно было компилятору, а не тебе. Не сказал бы, что в Rust нет такого (чего только стоит секс с borrow checker-ом зачастую), но если в Rust эти потуги вознаграждаются в итоге более строгой архитектурой и меньшим количеством хаков, то в С++ обычно выходит с точностью да наоборот)

Не интересно комментировать остальное, кроме вот этого:

Тоже не скажу за всех, но Segmentation Fault на моей памяти самая частая причина почему падает плюсовое приложение.

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

2. любая безопасная работа требует чёткого понимания когда конкретно с чем конкретно работаем. Тогда пиши хоть на асме — не будет никаких проблем. Юниксы и прочие Линуксы, написанные на самых небезопасных С/С++ с годами аптайма тому пример (Винда диапазона NT4.0-2000-7 тоже совсем не так дурна на самом деле, как её любят представлять, и там точно микс С/С++ даже в kernel mode драйверах, но никсы в примере канонiчнее, конечно).

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

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

Ну в сишарпе самая частая ошибка — падение с NullReferenceException. Вторая — KeyNotFoundException.


  1. любая безопасная работа требует чёткого понимания когда конкретно с чем конкретно работаем. Тогда пиши хоть на асме — не будет никаких проблем. Юниксы и прочие Линуксы, написанные на самых небезопасных С/С++ с годами аптайма тому пример (Винда диапазона NT4.0-2000-7 тоже совсем не так дурна на самом деле, как её любят представлять, и там точно микс С/С++ даже в kernel mode драйверах, но никсы в примере канонiчнее, конечно).

Остается вопрос стоимости написания такого безопасного приложения. Один вопрос когда у вас сениоры с 10 годами опыта должны еще друг за дружкой следить и валгринды гонять на регрессе. И другое когда у вас джун с 1-2 годами опыта выдает продукт с теми же характеристиками.


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

А вот это миф. Собственно, раст хороший пример, когда с безопасностью можно еще и выиграть в производительности. Наглядный пример — бесплатные ссылки & вместо вполне себе платных shared_ptr. Безопасность и производительность абсолютно ортогональные вещи. То что в некоторых языках решают пожертововать одним ради другого не означает, что это основной закон мироздания.

Наглядный пример — бесплатные ссылки & вместо вполне себе платных shared_ptr.

А зачем использовать shared_ptr там, где и в плюсах можно использовать ссылки?

Тем, что & проверит множественность владельцев, то что никто не мутирует в это время нашу ссылку, то что она указывает на живой объект и не инвалидируется, в общем вот это все. А с ссылками никаких гарантий нет.

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

Разве в расте можно продлить время жизни? Как я понимаю это просто защита от dangling reference.

Да, судя по примерам кода, которые PsyHaSTe тут приводит, он либо вообще не понимает, что такое reference, shared/unique_ptr, либо специально всех в заблуждение вводит.

Наглядный пример — бесплатные ссылки & вместо вполне себе платных shared_ptr.

сколько раз вам нужно написать что в плюсах «вместо растовых ссылок» используются ссылки, а не shared_ptr?

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

Немного доставляют все эти речи о безопасном расте и стрельбе по ногам в C++.
В C/C++ когда-то решили, что за корректность семантики тех или иных действий на той или иной конкретной платформе отвечает программист и назвали это неопределенным поведением. Решение оказалось удачным, что показывает объем реально работающего кода на этих языках.
Прошло 40 где-то лет и в Rust на основании накопленного опыта придумали, как в C ограничить 3 вида UB в самом языке и еще несколько на уровне библиотеки и делать это во прямо во время компиляции, не дожидаясь рантайма. Смержили результат с OCaml'ом и презентовали публике. Как по мне — норм, пожалуй, лучше, чем то, что было. Хотя куча прелестей все-равно осталось.
Но всерьез напирать на какую-то мифическую безопасность одного или, наоборот, опасность другого…

Для раста формально доказано, что в сейф подмножестве вы не получите УБ. Если вы доверяете ансейфу в стд (он сейчас тоже валидируется формально, к слову), то получается что уб в ваше программе невозможно получить. Равно как и разыменование нуллов, и прочие неприятные вещи. Как по мне весьма существенная гарантия.

Вы не получите конкретное и ограниченное подмножество UB. Да, это «доставучее» подмножество, но Rust явно перечисляет, что он не считает запрещенных UB. С моей точки зрения авторы языка прАвы, а вот апологеты не совсем.

Вы вообще не получите UB. Ни подмножества, ни надмножества. Без ансейф раста получить уб невозможно.

Еще раз: посмотрите внимательно растомикон. В безопасном расте вы не получите:
Dereferencing null, dangling, or unaligned pointers
Reading uninitialized memory
Breaking the pointer aliasing rules
Producing invalid primitive values:
dangling/null references
null fn pointers
a bool that isn't 0 or 1
an undefined enum discriminant
a char outside the ranges [0x0, 0xD7FF] and [0xE000, 0x10FFFF]
A non-utf8 str
Unwinding into another language
Causing a data race

Это серьезный прогресс, но и не исчерпывающий список.

Всё это не получится сделать без ансейфа. Не получится разыменовать указатель, сделать невалидный примитив или некорректную строчку.

Ну, вообще-то можно, нопремер, при конверсиях из плавающей точки в int. Для борьбы с этим явлением сделали даже специальный ключик -Zsaturating-float-casts. Раньше (и сейчас без этого ключика, насколько я понимаю), можно было в safe code написать примерно следующее (сорян, я не растаман, простите очепятки):
let array_idx = 1e99999f64 as usize; // ой, не влезло, array_idx не определен
let val = some_array[array_idx]; // упс

как-то так. Насколько я вижу, соответствующий issue до сих пор не закрыт.

Ну это ICE, тут вопрос другой. От багов компилятора ни один язык не защитит.

Не то чтобы это баг компилятора, просто обычный случай протекания абстракции. Перед конверсиями такого рода в общем случае требуются проверки, но на каждый чих их делать дорого, отсюда и вот эта раскоряка в лице этого флажка. Когда говорят "математически доказано", "проверено электроникой" и так далее, забывают, что математическое доказательство относится к МОДЕЛИ, а модели никогда не отражают суровую реальность идеально.

Попробовал проверить это локально, не ругается ли клиппи, но у меня даже собрать не вышло: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=b9d3bb21be0d1c2a5cfbc0a46749fb06


Когда говорят "математически доказано", "проверено электроникой" и так далее, забывают, что математическое доказательство относится к МОДЕЛИ, а модели никогда не отражают суровую реальность идеально.

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

Я там девяток слишком много нарисовал видимо, f64 столько не вмещает. Max double по-моему 1.7e+308 где-то. Когда я запускаю код в этом playground в release и debug, у индекса получаются разные значения, что как бы намекает, что как минимум одно из них мусорное, а, может, и оба.

Я же говорю — это не бага, а неизбежное зло. В таких конверсиях компилятор либо должен вставить ряд runtime проверок и зарулить производительность в ряде сценариев в минуса, либо пойти по пути C/C++ и оставить добавление проверок в каждом конкретном случае на усмотрение программиста (сиречь допустить UB). Это цугцванг.

Достаточно запретить такую конверсию, пусть разраб либо сам через ансейф реализует нужное ему поведение, либо даже не знаю что сделать.


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

Получится, если при такой конверсии float/double влезут в целевой целочисленный тип. Если разработчик уверен, что влезет — все нормально. Если не влезет — увы, будет печаль. Насчёт вынести это в unsafe — ну хз, выглядит, честно говоря, как недавняя попытка экопротестунов закрыть деревянными палетами дизельный генератор :)

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


В текущем виде каст смысла не имеет. Мало того что мы кастуем из флоата в инт в значение другой битности, так еще и значение не принадлежащее домену типа.

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

У меня в хаскеле есть минимум три функции с сигнатурой [которая после мономорфизации выглядит как] Double -> Int, называются floor, ceil и round. Какую из них надо выбрать под такой каст и почему?

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

к черту «потенциально», давайте «фактически»

Окей, кучу фактически стреляющего по ногам кода.

Окей, кучу фактически стреляющего по ногам кода.
ну уж нет, не отделаетесь. Вы написали:
Наглядный пример — бесплатные ссылки & вместо вполне себе платных shared_ptr

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

Ну вот давайте такой пример:


fn f<'a>(x: &'a i32) -> impl Fn() -> i32 + 'a {
    move || *x
}

fn main() {
    let x = 5;
    let y = f(&x);
    println!("{}", y());
}

На плюсах:


#include <memory>
#include <iostream>
#include <functional>

std::function<int(void)> f(std::shared_ptr<int> x) {
    return [&]() { return *x; };
}

int main() {
    std::shared_ptr<int> x(std::make_shared<int>(4));
    std::function<int(void)> y = f(x);
    std::cout << y() << std::endl;
}

При этом даже шаред_птр не очень спасут, потому что вот это будет ошибкой компиляции:


fn f<'a>(x: &'a i32) -> impl Fn() -> i32 + 'a {
    move || *x
}

fn main() {
    let y;
    {
        let x = 4;
        y = f(&x);
    }
    println!("{}", y());
}

А вот это — нет:


std::function<int(void)> f(std::shared_ptr<int> x) {
    return [&]() { return *x; };
}

int main() {
    std::function<int(void)> y(nullptr);
    {
        std::shared_ptr<int> x(std::make_shared<int>(4));
        y = f(x);
    }
    std::cout << y() << std::endl;
}
На плюсах:
даже с std::shared_pt без UB не смогли, ок, пишите на расте от греха подальше. Но всякую чушь нести не надо ибо аналогом на плюсах будет:
#include <iostream>
#include <functional>

auto f(int& x) {
    return std::function([&] { return x; });
}

int main() {
    int x = 4;
    auto y = f(x);
    std::cout << y() << std::endl;
}
даже с std::shared_pt без UB не смогли

Так про это и речь, не?

Если ты не можешь забить гвоздь в стену, а вместо этого попадаешь по пальцу, это не повод винить молоток. Это повод научиться забивать гвозди так, чтобы при этом не попадать по пальцам. Смотря на эти упражнения с захватом временных копий shared_ptr по ссылке, я где-то понимаю, откуда в растовских крейтах берутся эти десятки CVE.
Если ты не можешь забить гвоздь в стену, а вместо этого попадаешь по пальцу, это не повод винить молоток.

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

Если этот инструмент позволяет забивать гвозди только определенной марки и только под определенным углом, то какбе нельзя сказать что молоток ему прямо «уступает». Зависит от ситуации.
Так про это и речь, не?
во-первых, с него станется и специально эту багу посадить. Во-вторых, речь про другое. PsyHaSTe пытается доказать что в плюсах не существует способа добиться производительности rust. А способ есть, всегда, надо лишь головой думать, что и как ты делаешь. Если же хочется не думать, а клацать пока не соберется «корректная» программа, тогда да, раст безусловно лучше.
плюсах не существует способа добиться производительности rust

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

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

Ну так это ведь не совсем аналог, если я так понимаю пере выводом написать x = 5 то выведется совсем не то.


Проверить, к сожалению, не могу, потому что ваш пример у меня не компилируется: https://rextester.com/XRE91350


пишите на расте от греха подальше

Так я и сделаю.

Э, нет, не надо тут strawman использовать. Я сказал, что производительность и безопасность ортогональны, и не вам не всегда обязательно жертвовать одним в пользу другого.
нет, вы сказали:
Наглядный пример — бесплатные ссылки & вместо вполне себе платных shared_ptr.
что я собственно и прошу доказать.
Ну так это ведь не совсем аналог, если я так понимаю пере выводом написать x = 5 то выведется совсем не то.

Проверить, к сожалению, не могу, потому что ваш пример у меня не компилируется: rextester.com/XRE91350
мой пример отлично компилируется актуальной версией компилятора и стандарта. А вот «написать перед выводом x = 5» в расте не получится, из-за заимствования. Зачем вы спорите о языках ни в одном из которых вы не компетентны?
что я собственно и прошу доказать.

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


мой пример отлично компилируется актуальной версией компилятора и стандарта. А вот «написать перед выводом x = 5» в расте не получится, из-за заимствования. Зачем вы спорите о языках ни в одном из которых вы не компетентны?

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

Я показал по крайней мере один пример.
этот пример, даже будь он написан без ошибок, абсолютно ничего не доказывает
Этого достаточно для утверждения, что бесплатная безопасность — бывает.
я просил доказать не это. Я просил привести пример кода на расте, не выразимого в плюсах без доп. накладных расходов. Этого вы сделать не можете и даже не понимаете почему.
Я прекрасно знаю, что оно не компилируется. Примерно то же поведение я хотел бы в плюсах. Если не ошибку компиляции, то хотя бы эксепшн в рантайме.
так а если я хочу поменять значение переменной на которую существует ссылка? Использовать «весьма небесплатные» reference-counted обертки над ссылками?
я просил доказать не это. Я просил привести пример кода на расте, не выразимого в плюсах без доп. накладных расходов. Этого вы сделать не можете и даже не понимаете почему.

Я уже показал. Либо вы будете делать обертки, которые проверят это, либо потеряете в безопасности.


Другой пример — Option<NonZero<u32>> какой-нибудь.


так а если я хочу поменять значение переменной на которую существует ссылка? Использовать «весьма небесплатные» reference-counted обертки над ссылками?

Значит придется перехотеть и выразить свои мысли иначе. А то напоминает, как товарищь eao спрашивал "а как мне достать значение из опшна без проверки, если ну очень надо".

Я уже показал. Либо вы будете делать обертки, которые проверят это, либо потеряете в безопасности.
а. ваше утверждение было не про безопасность
б. если не думать головой что ты делаешь никаких оберток не напасешься
Другой пример — Option&ltNonZero&ltu32&gt&gt какой-нибудь.
Написать его аналог на плюсах несложно. Но никто не пишет… наверно потому, что не нужен?
Значит придется перехотеть и выразить свои мысли иначе
например?
А то напоминает, как товарищь eao спрашивал «а как мне достать значение из опшна без проверки, если ну очень надо».
а если я могу доказать что в этой ветке кода вышеупомянутый опшн никогда не будет пустым, зачем мне его перепроверять? Или так работает та самая zero cost безопасность которую вы пытаетесь доказать? Пока что только опровергаете
а. ваше утверждение было не про безопасность

То есть в фразе "производительность и безопасность ортогональны, и не вам не всегда обязательно жертвовать одним в пользу другого" нет ничего про безопасность? Ясно-понятно.


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

Верно, но чем меньше нужно про это думать, тем лучше.


Написать его аналог на плюсах несложно. Но никто не пишет… наверно потому, что не нужен?

Зиро-кост абстракцию такого вида без поддержки компилятора вы не напишете. А если мы затащим таким образом одну, вторую, третью фичи раста получим раст. Тогда что от плюсов останется?
А то, что нинужна — ну конечно, настоящий программист же всегда помнит, где у него что на нулл проверилось и в каком поле нолик означает отсутствие значение, а в каком — нет.


например?

Попытка модифицировать значение на которое кто-то держит ссылку — это ошибка. Можно сделать арены, чтобы ссылок не было, можно убрать мутабельность, вариантов много.


а если я могу доказать что в этой ветке кода вышеупомянутый опшн никогда не будет пустым, зачем мне его перепроверять? Или так работает та самая zero cost безопасность которую вы пытаетесь доказать? Пока что только опровергаете

Ну так вот пусть компилятор доказывает. А "мамай клянус" утверждениям я не верю.

То есть в фразе «производительность и безопасность ортогональны, и не вам не всегда обязательно жертвовать одним в пользу другого» нет ничего про безопасность? Ясно-понятно.
сколько раз мне надо тыкать вас в вашу же цитату «Наглядный пример — бесплатные ссылки & вместо вполне себе платных shared_ptr.» прежде чем вы признаете что облажались?
Зиро-кост абстракцию такого вида без поддержки компилятора вы не напишете
да ладно? Вот прям невозможно написать?
Попытка модифицировать значение на которое кто-то держит ссылку — это ошибка
Но это не ошибка.
Можно сделать арены, чтобы ссылок не было, можно убрать мутабельность, вариантов много.
я хочу писать простой, понятный и производительный код, а не плясать по прихоти компилятора
Ну так вот пусть компилятор доказывает
ценой стоимости в рантайме? Тогда ваше утверждение «Безопасность и производительность абсолютно ортогональные вещи» — ЛПП.
сколько раз мне надо тыкать вас в вашу же цитату «Наглядный пример — бесплатные ссылки & вместо вполне себе платных shared_ptr.» прежде чем вы признаете что облажались?

shared_ptr используется вместо сразу трех вещей, которые есть в раст: &, Rc и Arc. А теперь посмотрите, какая относительная частоат их использования в среденй растопрограмме. Понятное дело, что когда вам надо в рантайме считать ссылки то & не подойдет. По почти всегда можно посчитать всё статически.


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

Я так подумал, что вполне напишете. Ок, это я не то сказал.


Но это не ошибка.

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


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

Ну это тогда вам в питон. А то в С++ злой компилятор тоже не разрешит вместо числа строчку передать.


ценой стоимости в рантайме? Тогда ваше утверждение «Безопасность и производительность абсолютно ортогональные вещи» — ЛПП.

То что компилятор проверил в рантайме не нужно, потому что все уже проверено.

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

Вы же шарповик, если не ошибаюсь? А почему вы в C# не расстраиваетесь, когда меняется содержимое каких-то объектов, ссылки на которые у вас есть? Там же практически все объекты только по ссылке и доступны, кроме примитивных типов.

Весьма обижаюсь. Поэтому делаю только гет-онли интерфейсы и достаточно активно юзаю System.Collections.Immutable.

Сильно сомневаюсь, что это вам сильно помогает :) Вы, видимо, должны расстраиваться постоянно, потому что любой, я там не знаю, экземпляр NetworkStream будет менять свое состояние, пока вы держите на него ссылку, и вообще большинство классов из .Net Framework, у которых есть хоть какая-то внутренняя логика, будут себя так вести.

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




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

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

Ну а в расте это называется interior mutability и точно так же разрешена) А вот внешняя мутабельность — с ограничениями борровчекера. Вроде звучит логично.

Какой же это interior mutability, если этот NetworkStream постоянно дергает за ниточки третья сторона — ОС посредством оперирования состоянием его сокета? :) Да и честно говоря другим частям вашей же программы, которым вы передали копию ссылки (скажем, каким-то обработчикам событий), ничего не мешает выполнять из этого потока чтение, запись, менять настройки сокета и так далее?
Какой же это interior mutability, если этот NetworkStream постоянно дергает за ниточки третья сторона — ОС посредством оперирования состоянием его сокета? :)

Именно такая. Объект мутируется, хотя вам кажется, что нет.


А вообще проблема с тем, что &mut означает не то, что можно было бы подумать. Тут неплохо объясняется.

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

Да, немного неудачно сделали. Что ж поделать, идеального языка пока не придумали. Остается ждать, что через 10 лет появится что-то еще более новое и интересное.

А ничего, что вещи типа NetworkStream и должны менять свое состояние из-за внешних причин. И использовать их без учета таких изменений нельзя, и интерфейс у них для этого подходящий? Ну, в Rust это можно сильнее формализовать, в C/C++ с этим сложнее.
Как по мне, например, Rust, раскладывая все по аспектам, наглядно показывает, как много мы «подразумеваем», когда описываем простые вещи. Да, многословно, да, приходится писать с оглядкой на компилятор. Но, может быть, это и нормально, может, так и надо?
Конечно, должны. Это, собственно, был ответ на сентенцию «попытка модифицировать значение на которое кто-то держит ссылку — это ошибка». Разумеется, никакая это не ошибка, поэтому зачастую иметь несколько мутабельных ссылок на один объект — это не блажь, а необходимость.
Не соглашусь, притянуто за уши.
Средства ввода-вывода и другие виды взаимодействия с ОС или с железом напрямую — это выход за пределы языка и они всегда чем-то ограничиваются и во что-то заворачиваются.
А вот предоставляет ли язык абстракции, позволяющие их безопасно и правильно использовать или это только в документации прописано…

Какая связь между "безопасным и правильным использованием абстракций" и "попытка модифицировать значение, на которое кто-то держит ссылку — это ошибка" и почему одно из другого должно непременно следовать?

NetStream заполняется и опустошаются невидимым и несуществующем в языке способом. При чем тут ссылки, которые кто-то держит или нет?
Stream
как много мы «подразумеваем», когда описываем простые вещи

Это вы ещё на коданные, корекурсию и коиндукцию не смотрели.

shared_ptr используется вместо сразу трех вещей, которые есть в раст: &, Rc и Arc. А теперь посмотрите, какая относительная частоат их использования в среденй растопрограмме.
а заодно посчитайте частоту использования shared_ptr в с++-программе. И вы наверно удивитесь, но ссылки используются в с++ на несколько порядков чаще, чем shared_ptr.
Вот я бы очень расстроился, если бы мне кто-то дал ссылку а потом начал по ней что-то менять.
так я же не отдаю эту ссылку, ни вам ни кому бы то ни было. Она всё внутри той же функции (main), в которой лежит объект, ни в какие потоки она не передается, не инвалидируется и не инвалидирует другие ссылки, и ни в каких инстансах, которые переживут значение, не сохраняется. Эти условия, кстати, мог бы проверить и компилятор. То есть запись по этой ссылке совершенно безопасна и… по какой причине я не могу это сделать?
Ну это тогда вам в питон. А то в С++ злой компилятор тоже не разрешит вместо числа строчку передать.
не надо сравнивать типизацию и borrow checker. Нарушение контракта типа в с++ (обращение к инстансу T как к U) как правило является ошибкой, а в подавляющем большинстве исключений определено стандартом с++ (см. правила алиасинга и reinterpret_cast'а). Нарушение контракта borrow checker'а почти никогда не являлось бы ошибкой, если бы вообще было определено в расте.
То что компилятор проверил в рантайме не нужно, потому что все уже проверено.
у вас приступ?
не надо сравнивать типизацию и borrow checker. Нарушение контракта типа в с++ (обращение к инстансу T как к U) как правило является ошибкой, а в подавляющем большинстве исключений определено стандартом с++ (см. правила алиасинга и reinterpret_cast'а). Нарушение контракта borrow checker'а почти никогда не являлось бы ошибкой, если бы вообще было определено в расте.

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

Вот с этим я и не согласен. Кмк это ошибка того же рода, что строки вместо чисел передавать
В этой фразе правильно только «как мне кажется». С точки зрения стандарта с++, такое поведение определено. С точки зрения LLVM AST, в которое компилится раст, такое поведение тоже определено. Т.о. это ограничение существует только в rust, только ради безопасности и обходить его иногда приходится, зачастую ценой накладных расходов в рантайме. А это противоречит вашему утверждению «Безопасность и производительность абсолютно ортогональные вещи».

А можете заодно ответить на вопрос «То есть запись по этой ссылке совершенно безопасна и… по какой причине я не могу это сделать?»?
А можете заодно ответить на вопрос «То есть запись по этой ссылке совершенно безопасна и… по какой причине я не могу это сделать?»?

Была пародия, по-моему от КВНщиков, на известную песню Белого Орла, и она звучала примерно так — «потому что нельзя, потому что нельзя, потому что нельзя, ПОТОМУ ЧТО НЕЛЬЗЯ (громко басом)». Вот примерно поэтому :)
А можете заодно ответить на вопрос «То есть запись по этой ссылке совершенно безопасна и… по какой причине я не могу это сделать?»?

Потому что это могло бы позволить совершить UB в сейф расте, а это нарушило бы его гарантии.

Потому что это могло бы позволить совершить UB в сейф расте, а это нарушило бы его гарантии.
во-первых, правила борроучеккера можно было бы ослабить сохранив невозможность UB. Во-вторых, на одну ошибку которую он предотвратит, он 1000 раз только помешает, это замедляет разработку, а в ~трети случаев еще и рантайм. Было бы соотношение хотя бы 1 к 10 — было бы о чем говорить. А то получается что быстрее написать на плюсах, покрыть тестами и отревьюить; это помимо ошибок памяти еще и логические поймает.

Можете.
std::cell::Cell, смотрите функции get() и set() ЕМНИП

Если я правильно понял, std::cell::Cell позволяет только полностью подменять/вытаскивать значение, а не обращаться к нему по ссылке. Это может вызывать ненужные копии. RefCell, в свою очередь, вводит для ссылок runtime счетчик. Т.е. и то и другое просаживает производительность
не надо сравнивать типизацию и borrow checker

Э, субструктурная типизация же, ну.


Может, вам ещё и выражение предикатов в типах не надо сравнивать с типизацией?

Ну это тогда вам в питон.

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


Ну и про понятность, но то такое, субъективное.

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

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


Может, вы — сверхчеловек, не знаю.


ценой стоимости в рантайме?

Зачем?


Чем выразительнее система типов, тем больше проверок вы можете перенести в компилтайм.

Написать его аналог на плюсах несложно. Но никто не пишет… наверно потому, что не нужен?

Прочитал вас и вспомнил, как реализовывал что-то похожее минимум в трёх разных проектах, чтобы оверхеда от флага об optionality не было.

Я просил привести пример кода на расте, не выразимого в плюсах без доп. накладных расходов.

Лучше при всех таких спорах учитывать ещё стоимость модификаций и устойчивость к ошибкам при модификации и поддержке кода.


А вообще, ну, любой код, где компилятор обязан иметь в виду алиасинг, не?

Зачем?
Чем выразительнее система типов, тем больше проверок вы можете перенести в компилтайм.
речь о системе типов недостаточно выразительной, чтобы не запрещать многие корректные и оптимальные действия. Скажем, можно написать язык, оперирующий исключительно арифметическими операциями. В таком языке невозможно совершить ошибку работы с памятью, но почему-то таких языков маловато.
Лучше при всех таких спорах учитывать ещё стоимость модификаций и устойчивость к ошибкам при модификации и поддержке кода.
предположим я всё учитываю, не переживайте за это. Я хочу найти истину в споре.
А вообще, ну, любой код, где компилятор обязан иметь в виду алиасинг, не?
а что, хоть в одном компиляторе раста уже включили strict aliasing?
речь о системе типов недостаточно выразительной, чтобы не запрещать многие корректные и оптимальные действия.

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


Вопрос в том, насколько это серьёзное ограничение на практике.


а что, хоть в одном компиляторе раста уже включили strict aliasing?

Нет, LLVM (написанный на плюсах) ломается.

потому что все тайпчекеры, которыми захочется пользоваться, заведомо консервативны.
Вопрос в том, насколько это серьёзное ограничение на практике.
чекер о котором речь скорее «примитивен» чем «консервативен». И это серьезное ограничение
Нет, LLVM (написанный на плюсах) ломается.
а вы уверены, что дело только в llvm?
чекер о котором речь скорее «примитивен» чем «консервативен».

Это формальный термин, означающий, что существуют семантически корректные программы, отвергаемые тайпчекером.


а вы уверены, что дело только в llvm?

Нет, конечно, я же не знаю раст.

Это формальный термин, означающий, что существуют семантически корректные программы, отвергаемые тайпчекером.
Допустим, есть множества языковых конструкций языка X, опровергаемых тайпчекером Y и некорректных Z. Хотим: X >> Y == Z, имеем X > Y >> Z. Чем меньше X — Y, тем сложнее решать задачу в его пределах, что вводит пенальти на стоимость разработки и рантайм. Тайпчекер перестает быть актуальным если это пенальти выше такового от отсутствия тайпчекера.

Согласен. Однако смею считать, что множетство некорректных программ отвергаемых борровчекером >> множество корректных программ.

Однако смею считать, что множетство некорректных программ отвергаемых борровчекером >> множество корректных программ.
вы это на глазок оценили?
При этом даже шаред_птр не очень спасут, потому что вот это будет ошибкой компиляции:

А вот это — нет:
«А вот это» было бы корректным (т.к. shared_ptr в отличие от ссылки владеет объектом), если бы вы не посадили такой же UB как и в первом примере

А когда у вас есть


struct Foo
{
  int a;
  int b;
};

Foo foo;
int *ptr = &foo.a;
*(ptr + 1) = 10;

это уже стрельба по ногам или ещё нет? Компиляторы-то генерируют ровно тот код, что ожидается.


Вроде как.

это уже стрельба по ногам или ещё нет?

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

Так если я правильно понимаю, раст на такое счастье ругнётся.

Так если я правильно понимаю, раст на такое счастье ругнётся.
на какое счастье? PsyHaSTe утверждает, что растовые ссылки нельзя выразить в с++ без shared_ptr. Я требую от него пример, доказывающий это утверждение. Ваш пример ни на расте, ни с ссылками.
UFO just landed and posted this here
UFO just landed and posted this here

Это пока компилятор у вас паддинг там не вставил.

UFO just landed and posted this here
UFO just landed and posted this here
А может ли быть паддинг между элементами массива?

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


Но вот если вы напишете


struct Foo
{
  int x[4];
  int y[4];
};

то даже если паддинга не будет, записаь x[5] с целью обращения во второй элемент y (что я тоже встречал в продакшен-коде) — UB. Даже запись/чтение в x[4] UB.

UFO just landed and posted this here
alignas есть в c++11. Но всё равно лучше так не делать.

alignas ничего не гарантирует про пэддинг (по крайней мере, про его отсутствие).

Конкретно в коде примера ( тогда как это возможно, обеспечить выравнивание по 2м интам при этом умудриться вставить паддинг? ) или Вы имеете в виду в общем случае?
Конкретно в коде примера ( тогда как это возможно, обеспечить выравнивание по 2м интам при этом умудриться вставить паддинг? )

Берёте и вставляете паддинг размером в один инт.


Непонятно, конечно, зачем, но компилятор имеет право (хотя почему, понятно — всякие asan/ubsan так могут работать).

Всё я понял. Это настолько кажется не логичным, что мозг упорно отбрасывал этот вариант. Asan вроде зеркалирует память, и помечает зеркальную копию при попытках доступа.
А вот это миф. Собственно, раст хороший пример, когда с безопасностью можно еще и выиграть в производительности. Наглядный пример — бесплатные ссылки & вместо вполне себе платных shared_ptr. Безопасность и производительность абсолютно ортогональные вещи. То что в некоторых языках решают пожертововать одним ради другого не означает, что это основной закон мироздания.

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

Тоже не скажу за всех, но Segmentation Fault на моей памяти самая частая причина почему падает плюсовое приложение.

Большинство других ошибок можно поймать не падая, но разве они от этого перестают быть ошибками?

Конечно перестают. То, что у вас упало на тестах или во время отладки — это не состояние "упало", это состояние "в разработке". Я говорю про ошибки на продакшене и почти всегда это segmantation fault, memory corruption или memory leak.


От последнего Rust не спасает, но по крайней мере единственный известный мне простой способ memory leak в Rust — это циклические ссылки.


Ну еще остаются дедлоки из распространенных ошибок, Rust тут не помогает особо, разве что Mutex всегда под Guard-ом, что не так уж плохо.

Я имею в виду, что ошибки не ограничиваются падениями, независимо от того, продакшн это или нет. Если в программе произойдёт неожиданное деление на ноль или какой-нибудь ABA, она врядли упадёт, но и работать будет некорректно. Memory corruption и memory leak, между прочим, тоже редко приводят к падениям.


А в комментарии, на который я отвечал, Вы почему-то утождествляете ошибки и падения. В таком ключе SEGFAULT конечно естественным образом оказывается "корнем всех зол".

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

К тому же, безопасность работы с памятью — это устранение, ну… наверное 95% ошибок при программировании на С++?

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

Никаких ADL, SFINAE, аргументов по умолчанию, анонимных неймспейсов, static thread_local volatile переменных, неявных целочисленных и не только кастов, strict aliasing-а, noexcept, rvale, lvalue, xvalue, богомерского std::string, ещё более богомерского std::wstring, 20-ти видов инициализации, include-файлов, и бог знает чего ещё

с остальным могу согласиться (исторические причины, все дела), но что плохого в выделенных кусках-то?
— ADL помогает при написании generic кода, да и без него перегрузка операторов в неймспейсах бы не работала
— SFINAE — более общий и функциональный инструмент чем трейты раста.
— thread_local переменные могут быть полезны
— Неявные касты — чего в них плохого то? По сути, уменьшает бойлерплейт
— В любом языке с exception'ами noexcept в том или ином виде нужен

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

а система сборки раста умеет во все эти «Make, CMake и ещё ...» чтобы интегрировать его в уже существующие проекты, или интегрировать существующие проекты в него? А то получается, что энтропия-то не уменьшилась
только когда ты студент без стажа, пишущий лабораторные на си с классами. Хоть какой-то стаж + современные плюсы и ошибки памяти уже на несколько порядков реже чем логические.

Видимо всякие openssl и tox'ы пишут всякие студенты на лабораторках.

только когда ты студент без стажа, пишущий лабораторные на си с классами.

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


ADL помогает при написании generic кода, да и без него перегрузка операторов в неймспейсах бы не работала

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


SFINAE — более общий и функциональный инструмент чем трейты раста

… которое опять же порождает write-only код. Вспомните любые пляски с std::enable_if :) Это мощно, это круто, но это почти всегда делает код не очевидным.


thread_local переменные могут быть полезны

Бывают. Вот только семантически они вообще не отличаются от обычных и в большой базе кода вообще хрен разберёшь обычная это переменная или thread_local.


Я уж не говорю про то, что ключевое слово добавляет зависимость от libstd++. Огонь прост)


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

Ну… Неявные касты между числами ещё можно принять, но ведь есть ещё non-explicit конструкторы, которые порождают просто эпические приколы иногда)

А я бы и между числами запретил бы неявные касты. Ошибки сравнения signed/unsigned встречаются слишком часто (в статьях, в примерах ошибок)
Или когда ты поддерживаешь систему, написанную давно может даже и студентами
блин давайте может тогда каждый день переходить на новый яп, ведь на нем нет написанного легаси?
А эта фича действительно нужна? Она и вправду может облегчить иногда написание кода, как жаль, что код приходится в основном читать.
без неё написание некоторого кода невозможно. Например, использование в библиотеке перегрузок функций для пользовательских типов в пользовательских неймспейсах.
… которое опять же порождает write-only код. Вспомните любые пляски с std::enable_if :) Это мощно, это круто, но это почти всегда делает код не очевидным.
Я говорю об SFINAE как о методе решения задачи статического полиморфизма. Да, синтаксически он плох, впрочем, это поправят концепты. А может он больше, чем трейты.
Вот только семантически они вообще не отличаются от обычных и в большой базе кода вообще хрен разберёшь обычная это переменная или thread_local.
go to definition?
Ну… Неявные касты между числами ещё можно принять, но ведь есть ещё non-explicit конструкторы, которые порождают просто эпические приколы иногда)
И есть еще операторы явного/неявного каста. Это может быть и порождает неочевидный код, но как минимум синтаксически удобнее чем писать as VeryLong::TypeLiterallyNooneCares::About.
без неё написание некоторого кода невозможно. Например, использование в библиотеке перегрузок функций для пользовательских типов в пользовательских неймспейсах.
Мне кажется, что как только возникла потребность в генерации сложных алгоритмов на шаблонах (а это лет эдак 15 назад) нужно было придумывать полноценный метаязык кодогенерации (императивный или декларативный, а лучше оба) с нормальными читаемыми сообщениями об ошибках, а не развивать кучку «грязных хаков» которые в итоге получились, с сообщениями об ошибках, которые фиг прочитаешь.
И кодом, для чтения которого нужно лет эдак 5 опыта.
блин давайте может тогда каждый день переходить на новый яп, ведь на нем нет написанного легаси?

Если "каждый день" — это раз в… эээ… ну лет 30 плюсы существуют? Даже если взять начало от релиза С++98, всё равно выходит прилично. Как по мне наоборот выглядит немного наивно продолжать писать на языке с учётом всех его недочётов, появившихся из-за проблем железа эпохи динозавров.


без неё написание некоторого кода невозможно

Точно ли это правильный путь к написанию действительно понятного кода? Всё "невозможные" случаи, что приходят в голову, мягко говоря, пахнут.


Я говорю об SFINAE как о методе решения задачи статического полиморфизма.

А я говорю о SFINAE, который как только не используют :) Я бы рад сказать всем "хватит", но ведь не послушают :) Опять же в том же Rust та же самая проблема решается совершенно понятнейшим образом, не требующим SFINAE.


go to definition

Допустим я написал код в расчёте на thread_local, а потом кто-то по каким-то неведомым причинам удалил thread_local) Компилятор даже warning-а не кинет.


Это может быть и порождает неочевидный код

Этой причины уже достаточно, чтобы отказаться от этой фичи, но


удобнее чем писать as VeryLong::TypeLiterallyNooneCares::About

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

Если «каждый день» — это раз в… эээ… ну лет 30 плюсы существуют? Даже если взять начало от релиза С++98, всё равно выходит прилично. Как по мне наоборот выглядит немного наивно продолжать писать на языке с учётом всех его недочётов, появившихся из-за проблем железа эпохи динозавров.
вы же понимаете что кол-во легаси кода на ЯП пропорционально его возрасту и популярности? И что оно же зачастую является причиной остаться на этом яп?

Если говорить о долгоидущих планах, в с++ можно завезти эпохи и выводить из обихода устаревшие его особенности. Да, процесс займет лет 15. Не думаю что долю с++ можно откушать раньше.
Точно ли это правильный путь к написанию действительно понятного кода? Всё «невозможные» случаи, что приходят в голову, мягко говоря, пахнут.
на ADL строятся механизмы расширения стдлибы. Они «пахнут»? У вас есть предложения лучше?
Опять же в том же Rust та же самая проблема решается совершенно понятнейшим образом, не требующим SFINAE.
в раст она решена не полностью. Например, в расте тип не может удовлетворять трейту, о существовании которого он не знает.
Допустим я написал код в расчёте на thread_local, а потом кто-то по каким-то неведомым причинам удалил thread_local) Компилятор даже warning-а не кинет
то есть ваш аргумент "Вася поменял 1 на 2 и всё сломалось, плохой компилятор"? Убирание thread_local не вводит UB если вы не возвращаете на объект ссылку (тут компилятор ругнется), а в остальном это логическая ошибка.
Я топлю только за явный каст всего чего только можно, а как это синтаксически мне не интересно особо. Я просто хочу всегда видеть такие касты.
но это ужасно непрактично. Увеличение объема кода из-за ненужных явных кастов уменьшает читаемость сильнее, чем явность типизации увеличивает.
в с++ можно завезти эпохи и выводить из обихода устаревшие его особенности.

Или просто начать отказываться от проектов на С++ в сторону проектов на Rust? Ну понятное дело не всем и не сразу, но лет за 15 можно и смоч.


У вас есть предложения лучше?

impl Trait for Type? И не городить невероятной магии.


расте тип не может удовлетворять трейту, о существовании которого он не знает.

И слава богу! Господи, да в целом это единственная вещь в плюсовых шаблонах, которая раздражает больше всего. В этом плане в С++20 хоть концепты завезут и на том спасибо.


то есть ваш аргумент "Вася поменял 1 на 2 и всё сломалось, плохой компилятор"?

Да, это мой аргумент. Но виноват правда не компилятор, а правила языка. Если язык мне позволяет такое чудить, то это плохо. TheadLocal переменные не должны выглядеть как обычные. Ровно как и atomic. Потому что иначе без go to definition вообще не разобраться что происходит в конкретном кусочке кода.


Увеличение объема кода из-за ненужных явных кастов уменьшает читаемость сильнее, чем явность типизации увеличивает.

У меня другой опыт :) Отчасти вы конечно правы, но проблема скорее в том, что в С++ для каста слишком громоздкие ключевые слова (один reinterpret_cast чего стоит).

Или просто начать отказываться от проектов на С++ в сторону проектов на Rust? Ну понятное дело не всем и не сразу, но лет за 15 можно и смоч.
для многих проектов на это понадобится куда больше 15 лет. Так а смысл тогда?
Господи, да в целом это единственная вещь в плюсовых шаблонах, которая раздражает больше всего
ваше мнение здесь иррелеватно. Плюсовые концепты де-факто могут больше чем трейты раста. И я могу привести пример когда это может быть нужно.
TheadLocal переменные не должны выглядеть как обычные. Ровно как и atomic.
потому что… лично вы так думаете?
У меня другой опыт :) Отчасти вы конечно правы, но проблема скорее в том, что в С++ для каста слишком громоздкие ключевые слова (один reinterpret_cast чего стоит).
часто можно написать To(from), и это будет явный вызов explicit конструктора/оператора преобразования. Но проблема-то в другом, в том, что иногда неявные касты нужны для реализации удобного и читаемого API. Можно много говорить о том, как ужасен std::vector, но без неявного каста v[i] <-> bool пользоваться им было бы попросту больно
Или просто начать отказываться от проектов на С++ в сторону проектов на Rust? Ну понятное дело не всем и не сразу, но лет за 15 можно и смоч.

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

Или увеличивает, потому что когда нужно гарантированно не присвоить int64_t к uint32_t, то кажется придется городить шаблонный класс с кучей операторов.
Ну и даже создатели языка считают, что приведения типов — зло, но нужное и лучше его делать явным.
Или увеличивает, потому что когда нужно гарантированно не присвоить int64_t к uint32_t, то кажется придется городить шаблонный класс с кучей операторов.
или -Wimplicit-int-conversion -Werror. Часть кастов всегда являются корректными и вполне могут быть неявными, например каст uint32_t в int64_t, float в double или Derived* в Base*.
Ну и даже создатели языка считают, что приведения типов — зло, но нужное и лучше его делать явным.
Пользователь же имеет контроль над user-defined conversions, может его по желанию не определять или помечать явным.
Неявные касты float->double->(сколько-там бит в сопроцессоре)
могут приводить к тому, что на разных платформах программа (например, генератор рельефа в игрушке) приводит к разным результатам. Так что и тут — спорно.
Поделитесь, пожалуста, ссылкой, где почитать про опции вроде -Wimplicit-int-conversion -Werror
Ну то есть включением этого предупреждения мы признаем, что неявные касты — это плохо?
Derived* в Base* — это наверное единственный «хороший» неявный каст, и то можно придумать, что это вообще не каст, так как Derived is a Base.
Пользователь же имеет контроль над user-defined conversions, может его по желанию не определять или помечать явным.

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