Если бы я делал, я бы делал так:
В каждой переменной хранится не только её значение но и история операций над ней (можно в компайл тайм). Дальше каждая операция добавляет ограничение на значение переменных. Если всё иммутабельно в функциональном стиле то должно работать. Например мы пишем m = a % b и мы знаем что |m| < b. В плюса это было бы тип int который хранит число, а тут ещё эту информацию (a<|b|) дополнительно храним в переменной m. Далее мы уже можем вызывать функцию f(x,y) которая принимает не только int, а некий x на который наложено ограничение что он меньше y как f(m,b).
Пример в статье это в каком то смысле "противоречие", но с другой стороны в стандарте не написано что if constexpr (false) никогда не должно выполняться, хотя было бы логично ожидать что это вытекает из стандарта.
Вообще для чиений дискуссий про язык а против языка б которые последнее время всплывают на хабре, я был бы рад иметь узнать какую то теорию языков.
Возможно и да. Потому что там трюк похожий на рекурсивное определение шаблонов известный в плюсах уже давно. Т.е мне кажется все эти приколы метапрограммирования должны быть систематизированы уже где то.
А вообще как проверяется стандарт с++ на отсутствие логических дыр? Есть ли какое то доказательство что он не противоречит сам себе? Иногда такое впечатление что в него добавляют фичи метапрограммирования а только потом узнают о последствиях и всяких багофичах.
Почему вы взяли что там баг? Просто какое то кастование буферов, скорее всего будет ненужно когда завезут const generics наконец и будет нормальные GenericArray в std.
Отвечу сам себе. Создание и удаление временного объекта для f(unique_ptr<T> p) лежит на стороне вызывающего. Поэтому в с++ реализация функции f не вызывает конструктор и деструктор. В расте происходит по другому, при f(p: Box<T>) ответственность за удаление объекта лежит на самой функции f поэтому мы видим dealloc в асм выхлопе. Естественно на всю программу количество new/delete в случае с++ и раст не изменилось (при условии что с++ компилятор выкинет delete nullptr для moved-out объекта). Далее было бы интересно подумать какие это дает плюсы и минусы для оптимизации сложной программы где есть много подобных вызовов (так же в случаях f(f(f(p)))). Хотя если всё заинлайнилось то по-идее разницы быть не должно.
Согласен, имхо изначально была история запутанная: Типа давайте сделаем чтобы внешне было как на си только ооп, потом такие — блин у нас тут гораздо больше сущностей чем в си, ладно наделим каждую строчку дополнительным скрытым смыслом.
Очевидно что можно писать вокруг каждого unsafe if-ы и проверять инварианты.
Но во-первых даже если так делать (но вдруг забыть) фактически (т.е. место в коде в котором нужен патч) ошибка будет в if-ах а не в unsafe. Т.е. уже история с грепом по unsafe это некое преукрашательство.
А во-вторых unsafe затем и нужен что я могу отключить проверки для ускорения программы при реализации хитрого алгоритма, когда гарантии следуют из вышестоящей логики алгоритма, а не тупого ифа строчкой выше. Например я делаю тип ValidIndex который я могу отдавать наружу пользователю либы но через инкапсуляцию я как писатель либы знаю что в нём будет всегда хороший индекс, тода я могу в своей либе опустить проверки при использовании ValidIndex. Это пример надуманный.
Но например в std большое количество unsafe, и доказать что там работает всё коректно т.е. внешний апи safe было не быстрой задачей. Тут кидали ссылку где-то выше (может и вы сами) на статью.
Теперь мы приходим к моему тезису изначальному "значит надо проверять всю программу" — естественно не прямо всю целиком, а какой то модуль или класс, в простых случаях действительно только функцию или только 5ть строк, где происходит заворачивание ансейф в сейф.
Если бы я делал, я бы делал так:
В каждой переменной хранится не только её значение но и история операций над ней (можно в компайл тайм). Дальше каждая операция добавляет ограничение на значение переменных. Если всё иммутабельно в функциональном стиле то должно работать. Например мы пишем m = a % b и мы знаем что |m| < b. В плюса это было бы тип int который хранит число, а тут ещё эту информацию (a<|b|) дополнительно храним в переменной m. Далее мы уже можем вызывать функцию f(x,y) которая принимает не только int, а некий x на который наложено ограничение что он меньше y как f(m,b).
Сколько времени можно будет сэкономить :)
Пример в статье это в каком то смысле "противоречие", но с другой стороны в стандарте не написано что if constexpr (false) никогда не должно выполняться, хотя было бы логично ожидать что это вытекает из стандарта.
Вообще для чиений дискуссий про язык а против языка б которые последнее время всплывают на хабре, я был бы рад иметь узнать какую то теорию языков.
Возможно и да. Потому что там трюк похожий на рекурсивное определение шаблонов известный в плюсах уже давно. Т.е мне кажется все эти приколы метапрограммирования должны быть систематизированы уже где то.
А вообще как проверяется стандарт с++ на отсутствие логических дыр? Есть ли какое то доказательство что он не противоречит сам себе? Иногда такое впечатление что в него добавляют фичи метапрограммирования а только потом узнают о последствиях и всяких багофичах.
Разве в расте можно продлить время жизни? Как я понимаю это просто защита от dangling reference.
Изначальные поэмы вряд ли были из шедевров русской литературы, поэтому как раз в оригинале все отработало нормально;)
Автор не запускал код, потому что для utf-8 надо считать количество символов так
Иначе форматирование
^съедет.Ксати почем для си не включили подсветку синтаксиса или это хабр чудит?
Раст не настолько крут чтобы проверять алгоритмы хеширования на наличие легко-генерируемых коллизий :)
Почему вы взяли что там баг? Просто какое то кастование буферов, скорее всего будет ненужно когда завезут const generics наконец и будет нормальные GenericArray в std.
dynamic_cast в раст есть.
Отвечу сам себе. Создание и удаление временного объекта для
f(unique_ptr<T> p)лежит на стороне вызывающего. Поэтому в с++ реализация функцииfне вызывает конструктор и деструктор. В расте происходит по другому, приf(p: Box<T>)ответственность за удаление объекта лежит на самой функцииfпоэтому мы видимdeallocв асм выхлопе. Естественно на всю программу количество new/delete в случае с++ и раст не изменилось (при условии что с++ компилятор выкинетdelete nullptrдля moved-out объекта). Далее было бы интересно подумать какие это дает плюсы и минусы для оптимизации сложной программы где есть много подобных вызовов (так же в случаяхf(f(f(p)))). Хотя если всё заинлайнилось то по-идее разницы быть не должно.Точно так же передадите значение в си функцию из раста.
вы упрощаете в своем примере, например тут описывается реализация arena https://exyr.org/2018/rust-arenas-vs-dropck/, и уже инварианты не такие тривиальные.
Я тоже написал на расте жсон/хмл/sql молотилку и тоже без ансейф и всё хорошо :)
Согласен, имхо изначально была история запутанная: Типа давайте сделаем чтобы внешне было как на си только ооп, потом такие — блин у нас тут гораздо больше сущностей чем в си, ладно наделим каждую строчку дополнительным скрытым смыслом.
Очевидно что можно писать вокруг каждого unsafe if-ы и проверять инварианты.
Но во-первых даже если так делать (но вдруг забыть) фактически (т.е. место в коде в котором нужен патч) ошибка будет в if-ах а не в unsafe. Т.е. уже история с грепом по unsafe это некое преукрашательство.
А во-вторых unsafe затем и нужен что я могу отключить проверки для ускорения программы при реализации хитрого алгоритма, когда гарантии следуют из вышестоящей логики алгоритма, а не тупого ифа строчкой выше. Например я делаю тип ValidIndex который я могу отдавать наружу пользователю либы но через инкапсуляцию я как писатель либы знаю что в нём будет всегда хороший индекс, тода я могу в своей либе опустить проверки при использовании ValidIndex. Это пример надуманный.
Но например в std большое количество unsafe, и доказать что там работает всё коректно т.е. внешний апи safe было не быстрой задачей. Тут кидали ссылку где-то выше (может и вы сами) на статью.
Теперь мы приходим к моему тезису изначальному "значит надо проверять всю программу" — естественно не прямо всю целиком, а какой то модуль или класс, в простых случаях действительно только функцию или только 5ть строк, где происходит заворачивание ансейф в сейф.
Кстати интересный пример. Как оно в c++ работает? Почему компилятор выкинул delete, потому что я не вижу heap аллокации тут.
Eсли бы я хотел иметь проверку
idx < arr.len()прямо перед обращением к индексу я бы сразу использовал safe метод!У вас
ifснаружиunsafeвнутриunsafeважно.открываем первый пример
unsafe ничего не знает про idx, он может прийти откуда угодно, значит надо проверять всю программу.