Исключения не вписываются особо, потому что обработка ошибок слизана с хаскеля или чего-то подобного, она работает вполне нормально, просто другой подход
Вообще-то в хаскеле есть исключения. Как и во многих других функциональных языках — в systemd треде я пояснял, что в ФП неизбежно возникает потребность срезать углы, обходя нормальный поток вызовов функций. В Rust нет способов легко описать подобный алгоритм. В ФП обычно «ошибка» — это не более чем некий алгебраический тип, который возвращает «найденная позиция или не найдено», «ответ на запрос или код ошибки». То есть, ошибка — это не совсем ошибка. а вариант успеха. Настоящие же ошибки плана «ответ не распознан» или «сервер не найден» обрабатываются иным механизмом.
обезьяны с обоих сторон баррикад, и те кто ратует за упрощение чтобы совсем спинномозговым программированием заниматься по шаблонам, и те кто любит впихнуть в язык побольше фич, чтобы потом на них весело подрываться
Странная у тебя диллема. А куда отправляют те, кто ратуют за упрощение, чтобы минимальным набором фич реализовывать максимально сложные алгоритмы?
В раст вкатиться и начать выдавать приличный код куда проще чем в С++, при этом по скорости он особо проигрывать не будет
Откуда инфа? «На расте нельзя ковырять вилкой двадцатилетний код» — это сомнительный аргумент, при желании можно найти и код на расте под старый компилятор, который уже никто не помнит как работает.
С одной стороны да, с другой – был уже один такой, которому нормальный ООП был поперёк жопы. Эйхом звали. И как он только не сдох от засранной тысячами разрабов кармы. В итоге мелкомягкие таки-впилили в TS классы
Ну тогда надо «разворачивать» все решения, которые библиотеки используют. В «идеале» и стандартные тоже. Для языков с виртуальной машиной надо включать исходники этой виртуальной машины
«В идеале» можно было бы включить в бенч специализированный ящик который бы решал задачу вызовом единственной функции. Или делать расчеты на GPU. Если бы так можно было, то и питон бы выехал на первое место. Программа на Си, занявшая первое место, хоть и сложная, но всё решение содержится в ней. В программе на Rust почти всё решение содержится в подключаемой библиотеке — по правилам такая программа должна была быть дисквалифицирована, но ее почему-то оставили. При этом полностью идеоматичного кода на Rust там нет вообще, хотя есть идеоматичный код, например, на Си — это к вопросу об объективности сравнения.
«Оставить долю UB» звучит как «быть немного беременной». Ну а если без аналогий, то мне кажется, что с меньшими гарантиям раст был бы менее интересен: в конце-концов С++ уже есть
Rust немножко беременный в любом случае, потому что в крупном проекте неизбежно потребует применения unsafe.
Гуглить статью «Закон сохранения сложности» на РСДН
Почитал статью. 20-30 минут жизни потратил на чтение сравнений коров с килограммами и рецептов плана «ведро воды херак туды, охапку дров — и плов готов». Автор декларирует закон сохранения сложности, и чуть ниже опровергает его, приводя пример с разными реализациями одной задачи, которые имеют сильно разную сложность — зачем тогда было изначально декларировать этот «закон»?
Почитал статью. 20-30 минут жизни потратил на чтение сравнений коров с килограммами и рецептов плана «ведро воды херак туды, охапку дров — и плов готов».
Я в своё время поступил умнее: понял идею из первого абзаца, а последующее умоблудие пропустил. :)
Проблема в том, что в Си в аналогичных случаях зачастую вообще не нужно ничего писать, поскольку компилятору не нужно объяснять, какой контейнер как мы раскрыли и как запаковали обратно.
Какой контейнер? Или я тебя не понимаю или ты постоянно «слегка» меняешь тему: сначала говорил о «бессмысленных усложнениях», потом про необходимость писать let mut (это ещё можно назвать «многословностью», но никак не усложнением), теперь вот распаковка контейнеров.
Ну и когда я писал на С++, то const auto& встречалось ну просто очень часто. В этом случае в расте будет просто let.
«Прекрасно» это будет, когда будешь копипастить функции для изменения типов.
Как это?
В либах на расте почему-то любят строить стенки из однотипных объявлений, хотя, вроде как и макросы даже в языке есть, а все равно не помогает.
Я бы сказал, что в среднем макросами стараются не увлекаться. Для публичного интерфейса макросы тоже не очень.
Ну да, такого сахара в расте нет. Есть RFC на тему, но видимо не считается приоритетным. И если честно, так оно и есть. Видел несколько библиотек, которые через макросы подобное делали.
Я приводил в начале треда пример с Arc, где с ходу получилось найти UB с дедлоком, которые закрыты assert-ами, но не до конца — дедлок остался.
Перечитывать тему откровенно лень. Это как снаружи Arc получить панику (assert)? Ну а если асерты там на внутренние инварианты, то я подозреваю, что они для разработчиков написаны и пользователь интерфейса не должен мочь их получить.
Ключ unsafe в C# в том, что он меняет семантику выражений внутри себя. Например, убирает проверки в математике.
Разве проверки в математике отключает не unchecked?
Ну и я вижу следующее:
The unsafe keyword denotes an unsafe context, which is required for any operation involving pointers
То есть, не просто изменение семантики, а выглядит весьма похоже на раст, где разыменовывать указатели можно только в ансейф блоках.
Не звучат, потому что С++ развивается не одним только гуглом.
Так и раст тоже.
Ну и если по честному, то тут важно не только кто числится в комитете, а может ли одна корпорация продавить фичу, которая нужна только им. Или ещё хуже: фичу, которая мешает другим.
Это прослойка к С с классами.
Да, но на возможность, скажем, вызывать шаблонные функции я бы и не рассчитывал.
При чем здесь С++? Речь идет про раст. Зачем вы пытаетесь переводить стрелки?
Хочется.
Да и можно подумать, что скорость раста сравнивается не с С++.
То есть, ошибка — это не совсем ошибка. а вариант успеха.
и поэтому они там не нужны особо, а в расте есть паника.
Странная у тебя диллема. А куда отправляют те, кто ратуют за упрощение, чтобы минимальным набором фич реализовывать максимально сложные алгоритмы?
Дилемма у вас, а не у меня, потому как таких инструментов нет. Если фич минимально, то придется писать простыни кода чтобы выразить простыми вещами сложные абстракции чтобы потом уже ими выразить нужные вам вещи. Это не отдельная категория, это скорее ближе к языкам с минимальными языковыми средствами.
Откуда инфа?
откуда инфа что нет?
«На расте нельзя ковырять вилкой двадцатилетний код» — это сомнительный аргумент, при желании можно найти и код на расте под старый компилятор, который уже никто не помнит как работает.
А что это должно было аргументировать? Смысл был в том, что с++ язык с богатой историей ничем не помогающий программисту, по истечению долгих лет, и не одной итерации улучшений все к чему мы пришли это очередная пачка абстракций и косметических улучшений с гайдлайнами и правками в стандарт, но практически язык все еще не особо помогает на себе писать, не особо противодействует попыткам писать на себе плохо, не пытается никак забрать на себя бремя запоминания тонкостей использования себя. Создатели языка не делают ничего качественно меняющего работу со своим языком, и все это преподносится как гибкость. Чем то мне в этом плане и жс напоминает, там тоже похожее положение, кривой язык запатченый со всех сторон и рекомендации как на нем надо писать чтобы не вышло совсем плохо. Вы же рабствуете на ЖС можете и его попробовать позащищать.
Какой контейнер? Или я тебя не понимаю или ты постоянно «слегка» меняешь тему: сначала говорил о «бессмысленных усложнениях», потом про необходимость писать let mut (это ещё можно назвать «многословностью», но никак не усложнением), теперь вот распаковка контейнеров Ну и когда я писал на С++, то const auto& встречалось ну просто очень часто. В этом случае в расте будет просто let
Очень тяжело обсуждать не опираясь на конкретные примеры. Естественно, «let mut» — это только малая доля модификаторов-контейнеров. В соседнем треде приводили пример wake.rs, где почему-то для виртуальной таблицы понадобилось писать 300 строк:
Может ты объяснишь, что каждая строчка здесь несет ценный смысл и удаление ее из библиотеки недопустимо? Да, здесь почти нет никаких «let», зато сотня описаний типов аргументов.
«Прекрасно» это будет, когда будешь копипастить функции для изменения типов.
Как это?
Два варианта: либо писать просто копипастой, либо писать сложно обобщениями. Оба печальны в итоге: либо получается упомянутая стенка однотипных объявлений и вызовов функций, либо трейт на трейте трейтом погоняет, и все это (как в wake.rs) только для того, чтобы дернуть одну функцию.
Перечитывать тему откровенно лень. Это как снаружи Arc получить панику (assert)? Ну а если асерты там на внутренние инварианты, то я подозреваю, что они для разработчиков написаны и пользователь интерфейса не должен мочь их получить
Рекурсивная инициализация. То есть, переменная под Arc пытается читать саму себя при инициализации.
С какого количества строк начинается крупный проект?
Десятки тысяч строк, в данном случае. И я сейчас не имею в виду целенаправленное вынесение unsafe фич в отдельный проект — нет, unsafe фичи написанные для реализации проекта считаются единым целым с проектом.
То есть, ошибка — это не совсем ошибка. а вариант успеха.
и поэтому они там не нужны особо, а в расте есть паника
Ну в расте для паники изначально вообще не подразумевалась обработка. Там даже в отдельных функциях стандартной либы недавно всплывало повреждение памяти после паники.
Дилемма у вас, а не у меня, потому как таких инструментов нет. Если фич минимально, то придется писать простыни кода чтобы выразить простыми вещами сложные абстракции чтобы потом уже ими выразить нужные вам вещи. Это не отдельная категория, это скорее ближе к языкам с минимальными языковыми средствами
Я понял, в чем у нас расхождение — под фичами ты подразумевает некоторые универсальные и компонуемые примитивы. Я под фичами почему-то подразумевал что-то вроде питоньих специализированных конструкций, вроде тех же await/async, которые можно реализовать (и которые были реализованы раньше) без этого сахара.
Создатели языка не делают ничего качественно меняющего работу со своим языком, и все это преподносится как гибкость
Я то же самое могу сказать про Rust, только это происходит меньше лет, потому меньше масштаб. Вот как в язык в районе 2009-2010 года были заложены хорошие идеи, так с 2010 новые создатели языка и компилятора клали болт на удобство пользования им. Просто C++ в этом плане старше.
В соседнем треде приводили пример wake.rs, где почему-то для виртуальной таблицы понадобилось писать 300 строк:
Примерно половина - комментарии.
Может ты объяснишь, что каждая строчка здесь несет ценный смысл и удаление ее из библиотеки недопустимо? Да, здесь почти нет никаких «let», зато сотня описаний типов аргументов.
По прежнему не вижу проблемы в «описаниях типов аргументов». Это как тыкать в код на С и спрашивать почему так часто встречается void* и int.
Объяснять каждую строчку не готов, но большая часть кода там вполне очевидна.
Ну и я бы сказал, что важным критерием является как часто такой код встречается. Если нечто похожее приходится несколько раз писать в каждом проекте, то да - это проблема языка, что он не облегчает написание распространённого кода. Если же это экзотика, то и фиг с ним.
Два варианта: либо писать просто копипастой, либо писать сложно обобщениями.
Опять же, не понимаю суть претензии. А как иначе? В любом языке выбор будет именно такой, ну разве что если ограничения языка (например, нет дженериков) уменьшают твой выбор. Ну можно добавить ещё кодогенерацию (макросы или какую-нибудь внешнюю).
Ты подводишь к мысли, что в расте такие сложные дженерики, что ими лучше не пользоваться? Так прямо и скажи. Правда я не соглашусь. (:
Вон в С++, на первый взгляд, шаблоны проще: не надо расписывать ограничения, никаких тебе «трейт на трейте трейтом погоняет». Вот только к этому «простому инструменту» постоянно прикручивают всякие усложнения. Видимо не всё так просто?..
трейт на трейте трейтом погоняет, и все это (как в wake.rs) только для того, чтобы дернуть одну функцию.
Во первых, не одну. Во вторых, в этом коде как раз никаких трейтов почти и нет. Ну как обычно.
Мне откровенно лень сейчас искать, тем более, что поиск усложняется наличием всяких deny(unsafe_code) и прочего, что содержит слово «unsafe». Прям сейчас под рукой есть маленький проект (~20к строк), где ансейфа вообще нет, но исходники показать не могу.
Но вообще оспорить утверждение хочется. Если когда-нибудь будет не лень, то дам знать. (:
И я сейчас не имею в виду целенаправленное вынесение unsafe фич в отдельный проект — нет, unsafe фичи написанные для реализации проекта считаются единым целым с проектом.
Это ты заранее заготовил путь к отступлению, если такой проект найдётся? Мол если в самом проекте нет ансейфа и даже во «внутренних» библиотеках тоже, то в хоть какой-нибудь из используемых уж точно найдётся. Кстати, в чём тут вообще принципиальная разница брать готовое (с ансейфом внутри) или самому завернуть абстракцию в модуль/библиотеку?
Ну и в целом я не считаю ансейф проблемой. Если он вынесен в отдельную библиотеку (с безопасным интерфейсом и тестами) - так вообще замечательно. Опять же, в реальной жизни может понадобится сишную либу использовать (а ffi - unsafe) или бывает удобно пометить трейт как ансейф. В общем, это удобный инструмент и избегать его «просто потому что ансейф» смысла мало. Очень может оказаться, что твоё утверждение окажется справедливым. Но я всё равно считаю, что растовый (явный) ансейф намного лучше сишного.
для виртуальной таблицы понадобилось писать 300 строк
Не 300, а 100 - остальное комментарии, пустые строки и фигурные скобки на отдельной строке. И не для виртуальной таблицы вообще (для этого есть трейт-объекты), а для интерфейса к waker’у тасков, предоставляемого разными асинк рантаймами, который должен быть нетипизированным. Сама виртуальная таблица с функцией для её создания занимает 20 строк из которых 6 - атрибуты специфические для stdlib и 3 - закрывающие фигурные скобки.
Остальное - определение типизированных безопасных интерфейсов к этой vtable и вспомогательные методы вроде Debug.
Вот в gcc11.1 есть libstdc++-v3\include\std\ranges на 3000 строк. Нет желания выкинуть их и писать всё циклом for или обосновать необходимость каждой строчки? Ну так и тут сырую vtable не стали выставлять.
Ну в расте для паники изначально вообще не подразумевалась обработка. Там даже в отдельных функциях стандартной либы недавно всплывало повреждение памяти после паники.
Все приходит с опытом, в хаскеле тоже принято придерживаться мнения что исключения не стоит использовать в чистом коде, и вообще стараться минимизировать, но совсем избежать все равно не выходит, вычислитель все-таки не абстрактный, а вполне конкретный.
Я то же самое могу сказать про Rust, только это происходит меньше лет, потому меньше масштаб. Вот как в язык в районе 2009-2010 года были заложены хорошие идеи, так с 2010 новые создатели языка и компилятора клали болт на удобство пользования им. Просто C++ в этом плане старше.
Удобство меня волнует в меньшей мере, мне важно чтобы язык не заставлял меня запоминать аспекты своего использования (хотя бы какую-то часть), и не компилировал проблемный код, в этом плане подвижки есть. С++ мне может предложить, поставить иде с линтером, санитайзер, вчитываться в ворнинги, и новые фантики на старый страх господень (поясню, сначала были указатели, потом их завернули в итераторы, потом к этому делу еще и умные указатели соорудили, теперь в помощь к итераторам у нас ренжи подоспели) все это классно, но сам язык все равно даже не думают менять, он как не помогал, так и не помогает писать на себе, лишь с завидным постоянством обнавляют гайдлайны хороших практик, на конференциях рассказываю о новых ужасах препарирования языка которые вскрылись на рабочих кейсах, все это сильно напоминает не работу, а изучение созданного чудовища, его повадок, поведения и т.п. Это ли инструмент для работы? А раст… тоже ничего хорошего, но хоть что-то и нет этого бекграунда многолетнего со всеми его фейлами, тут пока еще есть надежда.
В соседнем треде приводили пример wake.rs, где почему-то для виртуальной таблицы понадобилось писать 300 строк:
Примерно половина - комментарии
Чуть больше половины. И, тем не менее, 130 строк для описания «ничего» — это как-то странно.
По прежнему не вижу проблемы в «описаниях типов аргументов». Это как тыкать в код на С и спрашивать почему так часто встречается void* и int
Да, это как тыкать в код на Си и спрашивать «почему так часто встречаются void * и int», а также спрашивать «чем же тогда Rust лучше?». Не так давно я был уверен, что Rust еще и владение данными выводит самостоятельно, но нет — borrow checker-а нужно с ложечки кормить руками. Падать ниц пред языком я должен только лишь за безопасность указателей, что меня возмутило. (да, я понимаю, что есть некоторые другие плюшки и я утрирую, но всё же)
Объяснять каждую строчку не готов, но большая часть кода там вполне очевидна... Если нечто похожее приходится несколько раз писать в каждом проекте, то да - это проблема языка, что он не облегчает написание распространённого кода. Если же это экзотика, то и фиг с ним.
Код не просто очевиден — она ничего не делает и по сути не нужен, потому что это повторяющиеся объявления указателей функций и однострочные пробросы их вызовов. Об этом как бы и вопрос — зачем эти строчки там нужны? Я приводил пример стенок однотипных объявлений в Rayon, еще такое было в FFT. Я подчеркиваю, что это не просто какие-то оптимизации, вроде записи развернутых циклов или предварительно вручную подсчитанных констант — это объявления, которые не выполняют никаких алгоритмов, это тупо объявления для компилятора. Да, они не в каждой функции, но в каждой второй-третьей, грубо говоря.
Два варианта: либо писать просто копипастой, либо писать сложно обобщениями.
Опять же, не понимаю суть претензии. А как иначе? В любом языке выбор будет именно такой, ну разве что если ограничения языка (например, нет дженериков) уменьшают твой выбор. Ну можно добавить ещё кодогенерацию (макросы или какую-нибудь внешнюю)
Как в хаскеле — писать просто на обобщениях. Потому что там нет бесконечных ёлочек, потому что вывод типов работает на уровне функций. В чем причина популярности динамических языков? У них обобщения точно так же по умолчанию, но с диспетчеризацией в рантайме, что не есть хорошо. Практика показала, что все хотят писать:
fn sum(a, b) {
a + b
}
Всё. Чисто, просто, понятно. Ваши типы на каждой второй строчке, ёлки формальных контейнеров, модификаторы указатели на изменяемые/неизменяемые данные — почти никому не нужны. Но индустрия настолько застряла в средних веках, что не смогла выдать ЯП, который позволял бы писать программы просто, и при этом статически компилироваться с мономорфизацией. Причем, индустрия настолько утонула в плодах трудов инквизиции, объявившей ересью всё за пределами ООП, что в какой-то период времени даже статически компилируемые языки генерировали полиморфный рантайм, из-за чего медленно ползали и жрали много памяти (Java).
Ты подводишь к мысли, что в расте такие сложные дженерики, что ими лучше не пользоваться? Так прямо и скажи. Правда я не соглашусь
В расте обычные сложные обобщения, которыми привыкли пользоваться крестовики — никакого улучшения в этом плане раст не предложил.
Вон в С++, на первый взгляд, шаблоны проще: не надо расписывать ограничения, никаких тебе «трейт на трейте трейтом погоняет». Вот только к этому «простому инструменту» постоянно прикручивают всякие усложнения
Меня удивило, что в последних стандартах кресты таки начали чистить. В кои-то века. Ну и они переходят на типоклассы. которые являются более адекватной вариацией обобщений — но я сомневаюсь, что это можно достаточно годно реализовать, не поломав совместимости со старым C++.
Конкретно неограниченный рост числа трейтов/структур/прочих абстракиций в расте вызван именно помешанностью на безопасности указателей — что есть бессмысленно, поскольку проекты неизбежно приходят к unsafe, а перегруженность кода вспомогательными конструкциями приводит к возникновению алгоритмических ошибок, которые Rust совсем никак не помогает искать.
трейт на трейте трейтом погоняет, и все это (как в wake.rs) только для того, чтобы дернуть одну функцию.
Во первых, не одну. Во вторых, в этом коде как раз никаких трейтов почти и нет. Ну как обычно
Код является оболочкой над ровно одной функцией — это wake. Всё остальное — это служебные функции, которые обслуживают сами себя и имеют смыслом передачу единственного аргумента этой функции wake.
Прям сейчас под рукой есть маленький проект (~20к строк), где ансейфа вообще нет, но исходники показать не могу
Ну это на грани, можете начинать заготавливать ансейфы «на вырост». Вы же не используете в этом проекте ящики от васяна?
И я сейчас не имею в виду целенаправленное вынесение unsafe фич в отдельный проект — нет, unsafe фичи написанные для реализации проекта считаются единым целым с проектом.
Это ты заранее заготовил путь к отступлению, если такой проект найдётся?
Это я сейчас про поехавших, которые угробят проект, пролюбят все дедлайны по два раза, но напишут без ансейфов. Или что-то вроде приведенного выше примера с разыменованием указателей — в расте арифметика с указателями является безопасной операцией, а опасно только разыменование. Сделай «отдельным независимым» проектом одну единственную функцию — «безопасного» разыменования указателя, и тогда в «безопасном» расте можно будет творить любой трэшак. Зато формально всё безопасно же ж.
Кстати, в чём тут вообще принципиальная разница брать готовое (с ансейфом внутри) или самому завернуть абстракцию в модуль/библиотеку?
Вот и я о том же. Челы с becnhmarking game выставили напоказ программу, которая завязана на ящик от васяна. Пусть васян и проверенный, пусть он много труда вложил в отладку и доводку либы, но это по прежнему не std.
Ну и в целом я не считаю ансейф проблемой. Если он вынесен в отдельную библиотеку (с безопасным интерфейсом и тестами) - так вообще замечательно
И тогда единственная существенная разница между C++ и Rust — это что последний больше бьет по рукам за кривое применение интерфейсов unsafe либы. Стоит ли эта «фича» усложнения кода и замедления компиляции?
Кстати, sqlx (~50к строк) ансейф использует только для SQLite, zola (~17к) вообще не использует
В sqlx половина строк — это тесты, которые находятся прямо в сорцах. А половина от оставшейся половины — это объявления констант. В zola без тестов будет где-то 6 тысяч строк.
Вот в gcc11.1 есть libstdc++-v3\include\std\ranges на 3000 строк. Нет желания выкинуть их и писать всё циклом for или обосновать необходимость каждой строчки?
мне важно чтобы язык не заставлял меня запоминать аспекты своего использования (хотя бы какую-то часть)
Что? То есть, ты подразумеваешь, что Rust не заставляет тебя запоминать аспекты своего использоваться? Вот как идеи появляются в голове — так сразу их записываешь и они работают, да? Спасибо расту за это.
и не компилировал проблемный код
Еще раз: Rust никак не помогает обнаруживать алгоритмические ошибки. Потому он с радостью скомпилирует проблемный код.
С++ мне может предложить, поставить иде с линтером, санитайзер, вчитываться в ворнинги, и новые фантики на старый страх господень (поясню, сначала были указатели, потом их завернули в итераторы, потом к этому делу еще и умные указатели соорудили, теперь в помощь к итераторам у нас ренжи подоспели) все это классно, но сам язык все равно даже не думают менять, он как не помогал, так и не помогает писать на себе
Да, в этом и прикол крестов — вынести две трети фич языка, и на оставшихся спокойно писать. Для этого в любой уважающей конторе, пишущей на крестах, есть ревью кода.
Касательно же итераторов с умными указателями — весь сыр-бор крутится вокруг «небезопасного» стиля писания кода, с интенсивным повторным использованием указателей на структуры. Предпочитай значения указателям/итераторам, и будет твой коды безопаснее... Но и медленнее. Это вообще универсальное правило, независимо от языка, просто Rust сделал его частью компилятора и заставляет небезопасный стиль явно описывать. А кто тебя изначально заставлял стрелять себе в ногу?
А раст… тоже ничего хорошего, но хоть что-то и нет этого бекграунда многолетнего со всеми его фейлами, тут пока еще есть надежда
Я тоже так думал. Но теперь прихожу к выводу, что раст создан прожженными крестовиками, которые тщательно скопировали недостатки крестов, вроде медленной компиляции и обобщений, устранив лишь самые очевидные всем проблемы.
Цитирую: «Два варианта: либо писать просто копипастой, либо писать сложно обобщениями. Оба печальны в итоге»
1500 строк variant’а, чтобы в итоге сделать switch и cast - это нормально. А 100 строк waker’a, чтобы преобразовать типы и дернуть указатель на функцию, почему-то мозолят глаза.
1500 строк variant’а, чтобы в итоге сделать switch и cast - это нормально
Это не «switch и cast» — это фундаментальная структура данных, которая затыкает чудовищный изъян архитектуры языка C++. Конечно, точнее я мог бы провести оценку, если бы знал, насколько эффективно оно этот изъян затыкает.
И я ни в коем случае не спорю с тем, что в библиотеках C++ тоже любят делать подобное waker.rs.
Тип сумма в плане реализации это именно switch&cast для которого компилятор (или, с переменным успехом, библиотеки) предоставляют типобезопасный доступ. Абсолютно та же ситуация, что и с этим пресловутым waker’ом.
Что? То есть, ты подразумеваешь, что Rust не заставляет тебя запоминать аспекты своего использоваться? Вот как идеи появляются в голове — так сразу их записываешь и они работают, да? Спасибо расту за это.
Не утрируйте, я думаю, вы прекрасно понимаете о чем идет речь.
Еще раз: Rust никак не помогает обнаруживать алгоритмические ошибки. Потому он с радостью скомпилирует проблемный код.
Эх раз еще раз, еще много-много раз. Это пунктик такой, говорить очевидные вещи как аргументы дискуссии? От таких вещей нет спасения. Инструмент владения, контроль мутабельности, искусственные ограничения на этот контроль, аннотирование времени жизни для ссылок, вообще явное аннотирование против неявного «работу этой языковой особенности надо держать в голове». Вот это все, чем больше надо прописывать в коде тем лучше, чем чаще компилятор ругается что не может разобраться что имелось в виду без этих пометок, тем лучше. Это лучше еще по причине того, что человек проводящий ревью вашего кода не будет вынужден домысливать что имелось в виду имея на руках подобного рода аннотации. Вообще я считаю что стоит максимально уходить от любых «соглашений», «допущений», «неявного поведения», «списка правил работы» вместо явного аннотирования в коде этой работы. Да может получается многословно и выглядит как мрак, но С++ выглядит не лучше, тем не менее там над вами постоянно довлеет факт того, что что-то вы забыли или упустили из внимания или просто в виду общей сложности написанного упускаете из виду те самые языковые нюансы. Вы постоянно должны сверяться с гайдлайном и отвергать любые попытки писать не по нему, потому что скорее всего это приведет к проблемам.
Да, в этом и прикол крестов — вынести две трети фич языка, и на оставшихся спокойно писать. Для этого в любой уважающей конторе, пишущей на крестах, есть ревью кода.
На нем только и можно писать выкинув половину функционала, потому как если тянуть все, обязательно получится в итоге кровавый фарш.
Касательно же итераторов с умными указателями — весь сыр-бор крутится вокруг «небезопасного» стиля писания кода, с интенсивным повторным использованием указателей на структуры. Предпочитай значения указателям/итераторам, и будет твой коды безопаснее… Но и медленнее. Это вообще универсальное правило, независимо от языка, просто Rust сделал его частью компилятора и заставляет небезопасный стиль явно описывать. А кто тебя изначально заставлял стрелять себе в ногу?
Никто никого не заставляет, а продолжают писать плохо, не хотят, божатся, но продолжают, и дело тут вовсе не в желании или принуждении - инструмент плохой, соблазн большой, репутация «быстрый, мощный, гибкий, для профессионалов» привлекает толпы фанатиков, кто-то из них действительно может выдать нормальный код, но большинство надламываются над сложностью языка, но причислять себя к профессионалам продолжают и пишут с N итерации что-то сносное, как вы тут сказали по пути выкинув в итоге пол языка и действуя только проверенными методами.
Я тоже так думал. Но теперь прихожу к выводу, что раст создан прожженными крестовиками, которые тщательно скопировали недостатки крестов, вроде медленной компиляции и обобщений, устранив лишь самые очевидные всем проблемы.
любой язык низкого уровня похож на С и проблемы у него такие же как и у С, не удивительно что всех их это роднит.
на конференциях рассказываю о новых ужасах препарирования языка которые вскрылись на рабочих кейсах, все это сильно напоминает не работу, а изучение созданного чудовища, его повадок, поведения и т.п.
Тип сумма в плане реализации это именно switch&cast для которого компилятор (или, с переменным успехом, библиотеки) предоставляют типобезопасный доступ. Абсолютно та же ситуация, что и с этим пресловутым waker’ом
Если бы в крестах всё было так просто, то модуль variant не был бы такого размера. А большой он потому, что в крестах большой зоопарк типов данных, с которыми нужно творить самые разные вызовы конструкторов-деструкторов, move-copy. Там тысяча строк посвящена только реализации универсального тривально уничтожаемого контейнера, в который засовываются объекты, и конструированию таблиц виртуальных методов этих объектов. Сделай допущение «только самодостаточные объекты и никакого наследования» — и внезапно модуль худеет в разы.
Конечно, если это ты хочешь назвать «типобезопасность», то есть, аккуратно подставлять костыли под каждую кривизну языка — то да. Но C++ имеет наследие, с которым приходится считаться, а Rust создан с нуля, однако же, повторяет проблемы C++ и уже из утробы вышел на костылях. И это не может меня возмущать, когда мне пытаются впарить это язык как решение проблем C++.
Если нет поддержки компилятора, то приходится писать ручками и иногда много - это да.
Но серебряной пули нет. Компилятор поддерживает глобальный вывод типов - замечательно, типы можно не писать. Но начинаются чудеса с поломанными модулями, которые никто не трогал, при рефакторинге, скорость компиляции падает. Компилятор поддерживает сильную систему типов и глобальный вывод (как в идрисе) - замечательно, можно писать статически корректные программы. Но для сильных систем типов вывод типов неразрешим - приходится явно указывать типы функций, что в этом случае эквивалентно доказательству теорем, и нужно быть немного математиком, чтобы эффективно использовать такой язык.
И это не может меня возмущать, когда мне пытаются впарить это язык как решение проблем C++.
Я пока вижу возмущение, что раст не идеальный язык мечты, сочетающий несочетаемое.
захотел сделать uber-функцию, которая оптимально считает среднее для любых типов данных и последовательностей любой длины и посрался с чуваком, который предложил использовать скользящее среднее.
Что? То есть, ты подразумеваешь, что Rust не заставляет тебя запоминать аспекты своего использоваться? Вот как идеи появляются в голове — так сразу их записываешь и они работают, да? Спасибо расту за это.
Не утрируйте, я думаю, вы прекрасно понимаете о чем идет речь
Ну типа если измерять это между питоном, который позволяет забивать на всё по черному, и каким-нибудь хаскелем, который заставляет досконально понимать структуру типов, иначе ты не поймешь ничего в ошибке, которую неизбежно получишь при компиляции — раст находится где-то по середине, вынося мозг, но не сильно, нежно. Однако же, «не запоминать аспекты своего использования» — никак нет, твой код просто не скомпилируется, а если скомпилируется — так упадет с паникой, а не упадет — так будет ползать со скоростью питона из-за неконтролированных копирований.
Еще раз: Rust никак не помогает обнаруживать алгоритмические ошибки. Потому он с радостью скомпилирует проблемный код.
Эх раз еще раз, еще много-много раз. Это пунктик такой, говорить очевидные вещи как аргументы дискуссии? От таких вещей нет спасения
Есть спасение, называется «читаемость кода». Когда алгоритм прост и ясен, то шанс совершить в нем ошибку намного ниже. Кстати, в лекции Александреску ниже есть интересный фрагмент, где он приводит примеры криптоалгоритма деревьев из libstd++:
Казалось бы, все знают красно-черные деревья, а вот хрен там, реализация деревьев в libstd++ — это совсем другой зверь, и ты будешь вычитывать его часами, чтобы понять, как он работает. Я уверен, что на Rust-е можно писать чистый код, где алгоритм будет алгоритмом, а не стенкой вспомогательных объявлений с редкими строчками реальных действий. Эта же претензия есть и к C++, Rust сделал ситуацию только хуже, потому что теперь ко всем вспомогательным объявлениям добавились еще вспомогательные объявления для соблюдения безопасности операций с указателями. Ткни в любую растовую либу либу — большая ее часть будет вспомогательными объявлениями. Ткни в любую либу шаблонов C++ — там тоже большая часть объявлений будет вспомогательными.
чем больше надо прописывать в коде тем лучше, чем чаще компилятор ругается что не может разобраться что имелось в виду без этих пометок, тем лучше. Это лучше еще по причине того, что человек проводящий ревью вашего кода не будет вынужден домысливать что имелось в виду имея на руках подобного рода аннотации. Вообще я считаю что стоит максимально уходить от любых «соглашений», «допущений», «неявного поведения», «списка правил работы» вместо явного аннотирования в коде этой работы. Да может получается многословно и выглядит как мрак, но С++ выглядит не лучше
Я не понимаю, ты что, из языков писал только на крестах и расте? Неужели ты не писал никогда в языках с простыми примитивными структурами данных? Которые могут быть многословны по мере роста сложности программы, но остаются просты? Такой вот матерый каноничный пример — это Кобол, на котором даже бабушки пишут, и не испытывают проблемы с необходимостью что-то там нужно держать в голове, помнить инварианты, придерживаться соглашений.
C++ — плохой и неудобный язык, Rust — плохой и неудобный язык. Но Rust выгоден корпорациям, вот его и форсят. Сиха — плохой и неудобный язык, но при всем при этом он ближе к идеалу, потому что он проще. Завези в Си примитивное RAII, безопасные макросы (они же шаблоны), доработай систему типов, чтобы появились нормальные массивы и ADT, сделай замыкания — и можно выкидывать кресты с растом на помойку. На самом деле, это описание о-о-очень близко к подмножеству C++. И дальше от Rust. Проблема крестов здесь только в том, что в нем присутствует очень много фичей, которые не нужны и которые можно по ошибке или по упоротости задействовать.
А кто тебя изначально заставлял стрелять себе в ногу?
Никто никого не заставляет, а продолжают писать плохо, не хотят, божатся, но продолжают, и дело тут вовсе не в желании или принуждении - инструмент плохой, соблазн большой, репутация «быстрый, мощный, гибкий, для профессионалов» привлекает толпы фанатиков
А я бы посоветовал вспомнить историю рождения C++ — он совсем не с такой репутацией вкатывался на рынок. Он вкатывался как Си с классами. И нифига не мощный, и не гибкий, и не быстрый — и, тем не менее, это привлекало если не толпы фанатиков, то как минимум «толпы» манагеров, создающих спрос на рабочую силу. Это потом, в девяностых, помешательство перешло в терминальную стадию и приобрело знакомую нам форму. Я знакомился с C++ еще до появления C++11, и на тот момент это был неюзабельный треш. Современный же C++ позволяет писать вообще без классов.
Я тоже так думал. Но теперь прихожу к выводу, что раст создан прожженными крестовиками, которые тщательно скопировали недостатки крестов, вроде медленной компиляции и обобщений, устранив лишь самые очевидные всем проблемы.
любой язык низкого уровня похож на С и проблемы у него такие же как и у С, не удивительно что всех их это роднит
Как прожженный сишник, который даже на паскале писал как на Си, для меня вся эта чисто крестово-растовская муть выглядит совершенно дико и не похоже на Си. У Си есть проблемы, но они другие, хоть и в каком-то смысле похуже крестово-растовых.
Андрей Александреску показывает уровень разработчиков стандартной библиотеки Rust и показывает токсичное комьюнити Rust
Меня стремает тот факт, что он большую часть лекции обсуждает одно вычисление среднего значения. Когда мне нужны деньги, я просто беру их из банки Когда мне нужно посчитать среднее значение, я просто считаю среднее значение. Я никогда не задумывался, что вычисление среднего значения может стать проблемой в языке программирования.
Компилятор поддерживает глобальный вывод типов - замечательно, типы можно не писать. Но начинаются чудеса с поломанными модулями, которые никто не трогал, при рефакторинге, скорость компиляции падает
По поводу «внезапно сломавшихся модулей» — никто не запрещает ставить объявления типов там, где они нужны. Проблема в том, что C++ и Rust требуют объявлять типы там, где они не нужны — сильно чаще чем там, где нужны.
Скорость компиляции решается монолитной компиляцией — это же является единственным на сегодняшний день реализованным (пусть и криво) решением для ускорения в разы компиляции сорцов C++ с интенсивным использованием шаблонов.
Компилятор поддерживает сильную систему типов и глобальный вывод (как в идрисе) - замечательно, можно писать статически корректные программы. Но для сильных систем типов вывод типов неразрешим - приходится явно указывать типы функций, что в этом случае эквивалентно доказательству теорем, и нужно быть немного математиком, чтобы эффективно использовать такой язык
Закапывай Милнера, он уже 11 лет как умер. Локальный вывод рулит, и он, очевидно, быстрее глобального.
И это не может меня возмущать, когда мне пытаются впарить это язык как решение проблем C++.
Я пока вижу возмущение, что раст не идеальный язык мечты, сочетающий несочетаемое
Но фанбои преподносят Rust именно как язык мечты. А по итогу он недалеко от крестов ушел.
Но фанбои преподносят Rust именно как язык мечты. А по итогу он недалеко от крестов ушел.
Или недошёл. В Rust нет static if / if constexpr, так что туже функцию расчёта среднего из видео Александреску не реализовать на Rust. Только аналог через копирование кода…
Не так давно я был уверен, что Rust еще и владение данными выводит самостоятельно, но нет — borrow checker-а нужно с ложечки кормить руками.
Lifetime elision есть, но это действительно не «вывод владения данных». Тут язык поступает последовательно: лайфтаймы - это часть сигнатуры и «прячутся» только самые очевидные вещи. При возникновении ошибки в лайфтаймах компилятор, зачастую, подсказывает как можно пофиксить и по этим подсказкам бывает видна разница между «минимальном подходящим лайфтаймом» и тем, что «задумывалось на самом деле». Есть неплохая статья где (в том числе) об этом рассказывается.
это объявления, которые не выполняют никаких алгоритмов, это тупо объявления для компилятора
Почему ты думаешь, что «пробросы вызовов» для компилятора, а не для человека?
К слову о количестве методов: надо было не в строку пальцем тыкать, а на итератор посмотреть - их там ещё больше. Можно было без многих из них обойтись? Да. Но они добавляют удобства.
Всё. Чисто, просто, понятно.
Ага. Особенно понятно, когда это не примитивная sum, а условная hz_chto с кучей параметров и множественными выходами, которые возвращают разные типы. Нет уж, лучше я буду «спотыкаться» об типы. Не говоря уже о том, что они помогают не только читать код, но и (внезапно!) писать. Если результатом функции будет хотя бы impl Trait, то компилятор мне поможет, если вдруг что-то пойдёт не так, а не молча выведет какой-то ужас.
Ну и на хаскеле ты ведь не пишешь?.. А почему?
В расте обычные сложные обобщения, которыми привыкли пользоваться крестовики — никакого улучшения в этом плане раст не предложил.
Ну нет. Между шаблонами и дженериками всё-таки есть разница. Удивлён, что ты не сказал, что шаблоны проще - в каких-то случаях так и есть, как раз из-за любимой тобой утиной типизации.
Конкретно неограниченный рост числа трейтов/структур/прочих абстракиций в расте вызван именно помешанностью на безопасности указателей
Опять же, нет. Далеко не все трейты (может даже подавляющее меньшинство) имеют отношение к «безопасности указателей».
Кстати, в хаскеле, на который ты выше ссылаешься, везде классы типов. И что в этом плохого вообще? Трейт/type class - это грубо говоря интерфейс. Если они стандартизированы, то проще стыковать друг с другом библиотеки и писать обобщённый код.
Всё остальное — это служебные функции, которые обслуживают сами себя и имеют смыслом передачу единственного аргумента этой функции wake.
Да, служебные. И что?
И даже drop/clone тоже «имеют смыслом передачу единственного аргумента этой функции wake»?..
При возникновении ошибки в лайфтаймах компилятор, зачастую, подсказывает как можно пофиксить и по этим подсказкам бывает видна разница между «минимальном подходящим лайфтаймом» и тем, что «задумывалось на самом деле». Есть неплохая статья где (в том числе) об этом рассказывается
Где-то к середине статьи у меня сгорел мозг. Я не подозревал, что всё настолько плохо. То есть, компилятор может вывести время жизни для чистых функций в пару строчек, но ни на что сложнее он уже не способен.
Почему ты думаешь, что «пробросы вызовов» для компилятора, а не для человека?
Потому что для человека в wake.rs есть одна функция wake и один аргумент для нее. Всё остальное — бесполезная ментальная нагрузка, которая отвлекает от реализации алгоритма.
Особенно понятно, когда это не примитивная sum, а условная hz_chto с кучей параметров и множественными выходами, которые возвращают разные типы. Нет уж, лучше я буду «спотыкаться» об типы. Не говоря уже о том, что они помогают не только читать код, но и (внезапно!) писать. Если результатом функции будет хотя бы impl Trait, то компилятор мне поможет, если вдруг что-то пойдёт не так, а не молча выведет какой-то ужас
Если у тебя функция должна вернуть trait, но вернула что-то не то, тогда возникнет ошибка при использовании этого «чего-то не того».
Ну и на хаскеле ты ведь не пишешь?.. А почему?
Потому что это язык, тщательно спроектированный для того, чтобы на нем нельзя было писать программы с побочными эффектами. Где побочные эффекты — весь ввод-вывод, IPC, и пользовательский интерфейс. Показательно, что на хаскеле почти нет приложений с интерактивным интерфейсом. Я не отношусь к тем поехавшим, которые будут упорно отрицать очевидные факты только потому, что я этот язык выучил и он для меня как родной.
Между шаблонами и дженериками всё-таки есть разница. Удивлён, что ты не сказал, что шаблоны проще - в каких-то случаях так и есть, как раз из-за любимой тобой утиной типизации
Шаблоны — это просто название для механизма обобщенного программирования в C++.
Далеко не все трейты (может даже подавляющее меньшинство) имеют отношение к «безопасности указателей»
Считаем для std::string::String:
impl AsRef — 4 штуки
impl Borrow
impl BorrowMut
impl Clone
impl Deref
impl DerefMut
impl From — 16 штук (кто там говорил про девять конструкторов у крестовых строк?)
impl FromIter - 7 штук
impl FromStr
impl Index — 6 штук
impl IndexMut — 6 штук
Итаво — 45. Больше половины. Можно говорить о том, что какие-то функции From* и Index* имеют функциональное, а не формальное назначение, потому я согласен на ровно половину.
Кстати, в хаскеле, на который ты выше ссылаешься, везде классы типов. И что в этом плохого вообще? Трейт/type class - это грубо говоря интерфейс. Если они стандартизированы, то проще стыковать друг с другом библиотеки и писать обобщённый код
Интерфейсы могут выполнять полезную функцию, а могут быть бесполезными, как половина интерфейсов раста.
И даже drop/clone тоже «имеют смыслом передачу единственного аргумента этой функции wake»?
Они по сути делают «data->drop()» и «data->clone()», то есть, управляют временем жизни аргумента функции wake.