Комментарии 254
10 надмозгов из 10, опять нейросети тестируете на чейтателях?
Судя по комментарию ниже, вы правы — переводила действительно нейросеть.
я твердо выступал за C++ (версии С99)
Шта? В оригинале «I advocated hard for C++ (over C99)». Вы пропаганду-то хоть переводите правильно :) Наброшу на вентилятор — тут одна контора на букву M уже предпочла раст, и теперь у нее проблемы, а команду растаманов пришлось разогнать :)
Вообще по этому поводу нет беспокойства. Rust уже остановить невозможно. Тем более, что Mozilla никогда особо и не вкладывалась в Rust. Теперь же быстрее сформируют Rust Foundation, язык уже довно созрел для самостоятельной взрослой жизни.
Вы очень точно уловили суть спасибо.
Спасибо, хотелось бы больше деталей. Поделитесь ссылкой?
тут одна контора на букву M уже предпочла эффективных менеджеров программистам и менеджера-сову на пост CEO (успешно выгнав инженера), и теперь у нее проблемы, и инженеров пришлось разогнать :)
У вас слишком много фактических ошибок в предложении. Починил, не благодарите.
Ммм, вот оно как, оказывается. А то, что на момент начала пиления серво доля FF и Chromium на браузерном рынке была примерно одинаковой, а на момент забрасывания его пиления (сколько там сейчас в него коммитов в неделю, один?) доля Chromium больше примерно на порядок — это просто совпадение? Или тоже свалите все на менеджера-сову? А с моей точки зрения это больше похоже на то, что "инженеры" вместо того, чтобы пилить фичи и оптимизации и стараться держать FF on par с Chromium, просрали годы усилий на игру в RIIR. А то, что произошло с мозиллой теперь — закономерный итог всего этого. Браузер с долей на рынке в единицы процентов (и продолжающей уменьшаться) все менее и менее интересен с точки зрения поисковиков (того же гугла), платежи от которых составляют львиную долю бюджета Мозиллы. "Инженеры" сами вырыли себе яму.
Или тоже свалите все на менеджера-сову?
Да, сова все это время бросала еще больше ресурсов компании на написание еще одного "нужного" расширения в стандартной поставке, вместо наращивания этой самой доли. Только в последние годы начали немного наверстывать упущенное PR-компанией об упоре на приватность.
и стараться держать FF on par с Chromium
И что же было не on par с Chromium? rust и компоненты мозиллы на нем — показатель того, что технологически firefox оказался даже впереди хромого. Который, неизбежно, тоже начинает работу над компонентами на rust в своей кодовой базе.
Приведу пример из своего личного опыта. Я раньше пользовался на десктопе и на андроид-мобилке браузером Brave (на движке Chromium), но некоторое время назад разрабы там поломали синхронизацию, и я решил попробовать перелезть на мозиллу. Скажу прямо — когда Brave починил синхронизацию, я был просто счастлив перелезть обратно. На десктопе ещё туда-сюда, но на мобилке Мозилла — это тормозное, жрущее батарейку говно. И, судя по свободнопадающей который год рыночной доле мозиллы, так думаю не я один. На фоне этого заявления адептов, что "мозилла впереди хромого потому, что раст" просто смешны. Раст не дал мозилле никаких явных преимуществ, не помог отвоевать долю рынка, это просто-напросто смертельно дорого обошедшиеся шашечки.
На десктопе ещё туда-сюда, но на мобилке Мозилла — это тормозное
Эх, помню времена, когда 99% жалоб на тормоза были из-за отсутствия плавной прокрутки из коробки.
Но вы не поверите, ужасная поддержка Android — очередной менеджерский продолб. Там не только в этом проблемы и проблемы "тормозит и жрет батарейку" не от ущербности движка. Вместо того, чтобы сфокусироваться на технической части, они решили сфокусироваться на клепании 10 разных версий с разными интерфейсами, в том числе на движке хромиума.
Где цифры? Все это ваше субъективное восприятие реальности. Я так тоже могу сказать, ибо перелез на FF под Android не из-за любви к Mozilla, а потому что он мне оказался удобнее. И никаких тормозов и проблем с батареей, при том что иметь десятки и сотни открытых вкладок для меня норма.
Ммм, вот оно как, оказывается. А то, что на момент начала пиления серво доля FF и Chromium на браузерном рынке была примерно одинаковой, а на момент забрасывания его пиления (сколько там сейчас в него коммитов в неделю, один?) доля Chromium больше примерно на порядок — это просто совпадение?
Не вижу закономерности

команду растаманов пришлось разогнатьТолько одних растаманов?
Доля chrome стремительно росла до появления на свет servo, а доля firefox снижалась.
Закономерность в том, что с этой тенденцией мозилле надо было как-то бороться. Но вместо этого они предпочли годами гробить усилия на пиление движка на расте from scratch, что им скорее навредило — средний юзер не видел для себя от этого никакого профита и продолжал перебегать на Chromium. В результате произошло закономерное падение рыночной доли до таких значений, что движок так и остался недопиленным, а пилильщиков (да и не только их, конечно) уволили.
Теперь весь интернет заспамлен раст-пропагандой, и они могут говорить «ну не шмогли, не повезло, но как пытались!» :))
От себя добавлю, что Rust привлекателен еще тем, что дает больше средств контроля за кодом со стороны компилятора, чем другие императивные языки. У него хорошая система типов. Благодаря ей, в частности, многопоточнный код на Rust писать сильно проще, чем на том же C++ или Java.
Borrow checker не такой уж и страшный: когда к нему привыкаешь, уже и замечать перестаешь. При разработке прикладного ПО редко когда действительно приходится решать проблемы, связанные с размещением в памяти. Большую часть времени ощущение от разработки такое же, как и на языке с GC.
Я нашел много ошибок с выделением памяти у коллег на с++, но не могу найти их у себя. Поэтому перешёл на раст. Прямо каминг оут какой-то. Блин с такой пропагандой раста ему врагов не надо, достаточно друзей.
Ждём святой инквизиции. Веруешь ли ты в непогрешимость святого Раста и не исповедуешь ли ты богомерзкого с плюс плюса или еретического Си. Покайся пока не поздно…
Программистов на расте примерно в десять раз меньше, чем на с++.Любой популярный язык, в том числе и сам C++, когда-то проходил стадию «программистов на X примерно в десять раз меньше, чем на Y».
Ваша реакция понятна, Rust действительно наступает вам на пятки. Иначе не было бы столько эмоционального негатива в его адрес.
Действительно странно, что системщики не хотят снова становиться джунами только потому, что в Mozilla решили, что будет прикольно урезать зарплатные ожидания путём внедрения очередной якобы серебрянной пули
Лично я только выиграл в зарплате при переходе на раст. А разве где-то это иначе работает? При появлении новой технологии специалисты автоматически не возникают из воздуха — надо или искать энтузиастов или переучивать. И с какой стати соглашаться на меньшие деньги? Тем более, что прошлый опыт будет совсем не бесполезным.
только потому
Не знаю ни одного человека, который перешел на Rust именно по этой причине. Тут вы правы.
Самое забавное, что приходят школьники в программирование и носятся с очередой серебряной пулей. История их ничему не учит, потому что оно ее и не знают. В Японии в свое время потратили миллиарды на пролог. Он был объявлен панацеей от всех проблем программирования и языком пятого поколения. И где он теперь. Причем язык то неплохой, но со своими тараканами.
Вот честно, где вы видите агрессию от людей, защищающих Rust? Вот хотя бы в данных комментариях, перечитайте. Вся агрессия идет как раз от противников Раста.
… Насчитывают до одиннадцати тысяч фанатиков, которые в течение этого времени пошли на казнь, лишь бы не разбивать яйца с острого конца. Были напечатаны сотни огромных томов, посвящённых этой полемике, но книги Тупоконечников давно запрещены, и вся партия лишена законом права занимать государственные должности. В течение этих смут императоры Блефуску часто через своих посланников делали нам предостережения, обвиняя нас в церковном расколе путём нарушения основного догмата великого нашего пророка Люстрога, изложенного в пятьдесят четвёртой главе Блундекраля (являющегося их Алькораном). Между тем это просто насильственное толкование текста, подлинные слова которого гласят: «Все истинно верующие да разбивают яйца с того конца, с какого удобнее.»
— Дж. Свифт. Путешествия Гулливера
Время идет, а люди не меняются.
Наверное это потому, что вы не программируете. Тогда вообще не понятно, что вы делаете в данном обсуждении. Зашли поругаться?
Извините, но мне сложно представить себе программиста, для которого возможности языка, тот инструментарий и те гарантии, которые он дает — по значимости где-то на уровне цвета клавиатуры (хотя может быть для вас цвет имеет большое значение?).
Но раз так, тогда я вам рекомендую везде использовать Rust. Вам же все равно, а так вы поможете распространению языка )
Там покрытие тестами такое плотное, и дизайн проходит столько ревью, что с аллокациями проблем просто не может быть, нужно по коду доказать, что такого не может быть. Это медицинское оборудование. Может лет через десять. Ну и советы Ваши в данном случае просто забавны. Я там двадцать лет работаю. Мой код работает примерно на половине всех CT сканеров в мире (GE)
хотя может быть для вас цвет имеет большое значение
Любой кроме красного :)
Я очень мало могу рассказать. Только в очень общих чертах. Есть NDA. И любая информация только через специальных пресс атташе. Лучше я Вам не про программы, а про программистов:
Нам на работе понадобился фантом в форме человека (Для отладки томографа) заполненный водой и с тонкой кожей. Я предложил купить куклу в сексшопе и налить в нее воды.
Зависло на этапе оформления. «Кукла из сексшопа на нужды группы программистов, для отладки программы». Не прошло :(. Купили фантом за >10K
Могу ссылку на статью для примера о реконструкции, я там в соавторах, поскольку эту реконструкцию и писал
pubmed.ncbi.nlm.nih.gov/16467583
Вообще ничего
Ну а я очень много…
Кроме того, что реконструкция имеет малое отношение к формальной верификации
Ваше мнение, в данном конкретном случае, безусловно очень важно для меня.
Много. Например, что те кто испольует GIT они скорее мультисайт, то есть у них несколько мест разработки и скорее всего автоматическая сборка и линукс. Svn — они работают под виндовс.а графика директ-икс
А те кто на СВН: работают на ДОС, графики нет, используют Basic.
Почему нет?
А тесты тогда зачем?
Но это же очевидно. Тесты показывают что продукт делает. Формально доказывается то, чего продукт не делает.
Неа, не получится. Формально доказывая что продукт работает вы рискуете ошибиться дважды (в продукте и при формализации ТЗ, а если это АПК — то ошибка возможна ещё и при построении модели аппаратной части).
Ну а отсутствие нежелательного поведения обычными тестами не ловится в принципе.
Объём кода мешает. Меньше кода — меньше ошибок, просто из-за статистики.
Тут идеальны вовсе ручные тесты, поскольку они вообще не требуют формализации ТЗ или построения модели аппаратной части.
Вот про восстановление изображений в vi3dim и использованный там код я могу рассказать, что угодно в пределах разумного, как ее CTO, а GE или THALES нет, не могу.
Ещё раз — негатив не в адрес раста, а в адрес совершенно убогой и назойливой пропаганды типа этой статьи. Почему это пропаганда? Вот типичный пример:
И тем не менее, при проверке кода я все еще регулярно ловлю их на допущенных ошибках или коде полагающимся на неопределенное поведение (UB). Ошибки, которые они не допустили бы в Rust.
Таки допустили бы. "Интереснее всего в этом вранье то, что оно — вранье от первого до последнего слова." ©
P.S. Работайте тоньше, меньше безаппеляционного тона и вранья, очевидного любому человеку с бэкграундом, и сами не заметите, как весь "эмоциональный негатив" куда-то исчезнет.
Конечно, в unsafe-коде вы всегда сможете допустить UB. Но речь же в статье именно о тех местах, где разработчики C++ допускают по невнимательности UB, тогда как в Rust этого бы не произошло. Автор говорит, что ловит коллег на ошибках, которые они не допустили бы в Rust — очевидно, что это про те места, которые покрывает безопасный Rust.
Но даже если говорить про unsafe, когда компилятор вас заставляет его писать — невозможно не обратить на это внимание, не остановиться и не подумать, а чего, собственно, тут небезопасного — это тоже хорошо предостерегает от ошибок по невнимательности.
То, что такая ошибка происходит в unsafe блоке — слабое утешение.
Это не утешение, это — принципиальный момент, о котором я уже сказал в комментарии выше.
как правило (намеренно?) забывают приложить к этому утверждению сносочку "*в safe подмножестве языка", в результате (намеренно?) создают ложное впечатление
Вам везде мерещатся заговоры. Автор говорит о том, что в тех местах, где его коллеги допускали UB, используя Rust они бы UB не допустили. Очевидно, что речь идет о safe Rust, потому что в unsafe допустить UB можно. Вы занимаетесь каким-то буквоедством и выискиваете в тексе то, чего там нет: где сказано, что используя Rust в принципе невозможно получить UB? Зачем вы додумываете за автора то, чего он не говорит? Проблема C++ именно в том, что в нем легко можно получить UB на ровном месте, по невнимательности, не ожидая вообще, что тут может быть UB. С Rust такое уже не прокатит.
Вот только как пример автора, так и мой относятся к областям, где требуется прямая манипуляция адресами и паддингом (аллокаторы, санитайзинг адресов в сисколлах, и так далее) — грубо говоря, где требуется работать с адресами как с целочисленными значениями. Safe rust в принципе не позволяет такого.
О каком примере автора речь? Если вы про тот баг из NaCl — то Rust его бы поймал: попытка вычислить 1 << 32
в отладочной сборке приводит к панике. Независимо от того, safe там или unsafe.
В одном случае к некорректному выравниванию указателя потенциально приводит некорректный сдвиг, в другом — арифметическая ошибка. Причины разные, результат одинаковый — потенциальное обращение по невыравненному указателю. В обоих случаях могли бы помочь тесты, но их не было. Но в целом да, насколько я вижу в playground, попытка сдвигать u32 на константу 32 сразу приводит к ошибке компиляции, если сдвигать не на константу, то все зависит от того, происходит ли сдвиг в отладочной сборке или в релизной.
I still regularly catch them making bounds errors or relying on undefined behavior. Errors they wouldn't make in Rust.
… меньше безаппеляционного тона и вранья, очевидного любому человеку с бэкграундом...
Ого… поругайтесь на автора, он около 10 лет работал в Гугле (из 25-27 лет в ИТ), не знает о чем говорит, но вы похоже знаете лучше него. :))) (было бы полезно посмотреть на ваш «послужной список»)
www.youtube.com/user/cbiffle/videos
www.linkedin.com/in/cbiffle
github.com/cbiffle
Нормальный аргумент, когда оппонент занят тем же самым.
Да, в явном виде этого действительно не было. Однако лично я читаю "защищает от UB" как "защищает от большинства UB" или даже как "защищает от появления UB на пустом месте". Такое утверждение единичным примером с UB в кастомном аллокаторе (как будто я каждый день пишу аллокаторы!) не опровергается, в рассуждениях явно не хватает дополнительного аргумента о типичности либо необходимости подобного кода для программ на Rust.
И вот этот-то аргумент ничем кроме своего бэкграунда Капитан подкрепить не может. А ведь это далеко не очевидно, я вот со своим бэкграундом даже не могу представить чтобы я сделал в каком-нибудь проекте свой аллокатор, но не покрыл его тестами, в том числе проверяющими выравнивание...
А ведь это далеко не очевидно, я вот со своим бэкграундом даже не могу представить чтобы я сделал в каком-нибудь проекте свой аллокатор, но не покрыл его тестами, в том числе проверяющими выравнивание...
Я тоже. Но вот автор obstack почему-то этого не сделал…
лично я читаю «защищает от UB» как «защищает от большинства UB» или даже как «защищает от появления UB на пустом месте»Стоп, а к чему вообще Rust тогда? Ведь есть C++, который защищает от UB.
Вот как раз в С++ всякие UB способны появляться на пустом месте. Недавно ещё один способ получить UB появился — передать что-нибудь по ссылке в сопрограмму. И это после инвалидации указателей, использования после перемещения, thread::detach и подобных функций...
Ещё бы кто сам shared_ptr защитил:
std::shared_ptr<foo> empty;
empty->bar = 42; // UB
std::shared_ptr<foo> not_empty = ...;
baz(std::move(not_empty));
not_empty->bar = 42; // потенциальное UB
И нет, "всё тоже самое" в unsafe блоке в Rust провернуть нельзя. Потому что unsafe не выключает никаких проверок у стандартных примитивов-контейнеров.
Да, я могу обратиться к чужой памяти если буду действовать через указатель. Но пока я работаю с вектором через итератор — я не получу UB, даже в unsafe блоке. Rust просто не даст сделать активный итератор невалидным.
ну так и в C++ в таких случаях можно multithreaded shared_ptr
Использовать его вместо всех shared_ptr идея не очень, иначе можно его забыть использовать там где надо. В расте из разных потоков менять можно будет только синхронизированные примитивы.
Если кому-то интересно, то переводчика зовут https://www.deepl.com/translator. У https://translate.google.com/ перевод несколько другой получается. Но тут уж на вкус и цвет.
переводчика зовут www.deepl.com/translator
спасибо
Предпочитайте RustПредпочитайте не использовать повелительное наклонение в дословных переводах. Звучит ужасно.
Самый главный вопрос не раскрыт: поддерживает ли Rust BLM?
dev.to/aachh/nim-v-rust-4kh5
Собственно, Ним только зарелизился, и находится в начале пути.
Yes, Rust is more safe. I don’t really care.
После этих слов статью можно закрыть.
Кстати в тему же этого вопроса: пример про написание связных списков, мягко говоря, удивил. В 99,99% случаев никто в здравом уме и трезвой памяти не станет решать типовые и давно решенные задачи, когда уже есть множество готовых проверенных решений в виде STL, Boost, Poco и да-тысячи-их-на-любую-потребность. И если это хоть на 1% не так в предлагаемой альтернативе — её просто ещё рано толкать в прод и масс.маркет. А если так, зачем такой пример?
— Вот что реально было бы интересно узнать (ну, т.е. как «реально»… было бы совсем реально — уже давно бы сам узнал, но пока просто интересно в рамках расширения кругозора) — это с какими полезными и удобными возможностями С++ придётся неминуемо расстаться при переходе на раст. There's no free lunch, сколько это стоит?
Упоминание связных списков было не примером в пользу Rust, а объяснением почему нельзя считать его примером в пользу Си. Именно по той самой причине, которую вы привели — связные списки есть в библиотеке.
Объясняю: стандартный аргумент растохейтеров — "посмотрите, в Расте даже двусвязный список я не могу без извращений сделать". Возможно, это как-то связано с тем, что двусвязные списки любят спрашивать на собеседованиях, и вообще они превратились в эдакий "Hello, world!" в области структур данных.
Автор на это как раз и отвечает, что большую часть времени программист должен писать что-то новое, а не в сотый раз переписывать библиотечные структуры данных.
Не то чтобы "лучше", просто это другой аргумент, который автор не разбирал.
Кстати, а что не так с передачей аргумента в функцию?
doc.rust-lang.org/1.8.0/book/references-and-borrowing.html
Here’s the code:Но теперь, уже можно. Я хренею, еще не успел язык выучить, как он уже поменялся и офиц.учебник устарел. :fail:
let mut x = 5;
let y = &mut x;
*y += 1;
println!("{}", x);
This code gives us this error:
Вот так опять нельзя. Borrowchecker стал лучше и поэтому ваш пример компилируется т.к. фактического нарушения у вас нет (время жизни переменной y заканчивается сразу после *y += 1;) https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=c58504a8417accd7a57d65355ec6f23e
Доброе утро, он поменялся несколько лет назад. И официальный учебник тоже давно уже поправили, в новой версии этого примера нет (вы что, специально сохранили ссылку на старую версию чтобы было на что ныть?)
К слову, вызов функции и сейчас требует заимствования. Просто на момент вызова printf
переменная y
уже пропала, а потому использовать переменную x
снова можно.
И ещё момент: к передаче параметра в функцию эта ошибка ни малейшего отношения не имеет. Пока переменная y
жива, запрещён любой доступ к переменной x
, а не только передача в функцию. И это не блажь, именно этот механизм спасает итераторы от инвалидации.
вы что, специально сохранили ссылку на старую версию чтобы было на что ныть?Нет, когда у меня не получилось просто воспроизвести, я только что нагуглил: G «rust borrow variable», 1я же ссылка.
Собственно, предлагаю вернуться к упомянутым «извращениям».
P.S. printf как сила привычки =)
Есть автоматическое управление памятью, а есть ручное с разной степенью валидации. Третьего не дано. Берите то что без извращений.
посмотрите, в Расте даже двусвязный список я не могу без извращений сделать
Я привел свой пример «я не могу даже функцию без извращений вызвать»
И я не могу никак понять, почему передача параметра по значению, требует заимствования.
И это при наличии в языке шести, Карл, комбинаций передачи параметра: x, &x, mut x, &mut x, &mut &x, mut &x!
Она требует не заимствования, а его отсутствия. Пока переменная x
эксклюзивно заимствована — вы ничего с ней не можете сделать, в том числе передать по значению.
И это при наличии в языке шести, Карл, комбинаций передачи параметра
Из перечисленных вами конструкций только три являются способами передачи параметра: x
(по значению), &x
(по разделяемой ссылке), &mut x
(по эксклюзивной ссылке). Ведущий mut
никак на передачу параметра не влияет.
Кстати, у всех этих конструкций есть прямые аналоги в языке Си.
Ну вот верьте или нет, только после довольно небольшой работы с растом все проблемы с бч отпадают или решаются очень быстро. За исключением новых структур данных, где таки в любом случае надо всё хорошо продумать.
А где вы видите функцию? println!
— это макрос, который заимствует переданные параметры.
let mut x = 5;
let y = &mut x;
*y += 1;
println!("{}", &y);
println!("{}", &x);
let mut x = 5;
let y = &mut x;
*y += 1;
println!("{}", &x);
println!("{}", &y);
Почувствуйте разницу!
И такими дикими дизайнерскими решениями Раст наполнен прилично. Нарушается принцип наименьшего удивления.
Например, я вчера автоматически написал такой неверный код
fn foo(a: i32) -> i32
{
a;
}
Всё с принципом наименьшего удивления в порядке, если не заниматься формированием эксклюзивных ссылок на локальные переменные и сохранением их в локальных же переменных.
Всё-таки &mut
— это прежде всего инструмент для передачи параметров.
Кстати, а что с вашей функцией foo
-то не так?
Точка с запятой в конце. Но такой код все равно не скомпилируется, удивление это может вызвать только у тех, кто Rust видит впервые.
Я так в каком то другом случае по совету компилятора тасовал всякие варианты mut и ссылок, пока круг не замкнулся.
В таком коде в одном потоке проблем быть не должно. Прикол в том что &T
разрешено шарить между потоками, отсюда и более строгие правила.
Товарищ Siemargl, ваше рвение в изучении Rust похвально, но может, вы всё-таки прочитаете по нему учебник?
doc.rust-lang.org/1.8.0/book/references-and-borrowing.html
И выясняется, что тут не читаем — «тут рыбу заворачивали» =)
Кстати, ни из ссылки 1.8.0 (а Раст то 1.46), ни из титула не видно, что это устаревшая документация.
- Вас удивляют правила заимствования в языке, вокруг которых он фактически и выстроен;
- Вы не можете отличить вызов функции от вызова макроса;
- Вы не понимаете как работает
println!
и испытываете трудности с расстановкойmut
.
А вы точно книгу-то читали? Или это из разряда "смотрю в книгу — вижу фигу" у вас?
И да, я затрудняюсь писать на Раст, идиотский синтаксис и ограничения мне мешают.
Ну если наличие в программе GC или UB (на выбор) вам не мешает, то, наверное, и нет особой надобности использовать именно Rust. Синтаксис тут вряд ли играет какую-то существенную роль.
Я в свое время перешел на Rust потому, что мне понадобился статически типизированный язык без GC и с хорошей системой управления зависимостями. Синтаксис поначалу не нравился, но потом перестал испытывать с ним какие-либо неудобства. И так было со многими моими знакомыми: синтаксис нормальный, просто сначала он может быть непривычен.
А как вам вариант искать документацию по Rust на, внезапно, официальном сайте Rust, а не искать в гугле?
Вот я не понимаю, вы берете самые главные правила языка — правила заимствования, вокруг которых вообще выстроен весь его дизайн — и вдруг они у вас вызывают удивление. Вы точно понимаете, с чем вы имеете дело?
Например, я вчера автоматически написал такой неверный код
И что страшного произошло? Компилятор вам сказал:
error[E0308]: mismatched types
--> src/main.rs:1:19
|
1 | fn foo(a: i32) -> i32 {
| --- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | a;
| - help: consider removing this semicolon
Так сложно прочитать сообщение об ошибке и убрать ;
? Вот честное слово, детский сад какой-то.
Но, кстати, а чем точка с запятой то помешала?
А вот здесь, например, лишняя запятая почему то не помешала, где логика???
let arr:[i32;4] = [10,20,30,40,];
Да не очень то сложно, просто по привычке поставил.
То есть опять язык не выучили вы, а виноват Rust.
Но, кстати, а чем точка с запятой то помешала?
Точка с запятой после выражения отбрасывает его значение и возвращает ()
. И это написано в TRPL.
А вот здесь, например, лишняя запятая почему то не помешала, где логика???
А это для того, чтобы можно было писать вот так:
let strings = [
"there might be many difficult tasks",
"on the path of one who only started to learn programming",
"but reading through documentation",
"is certainly not one of them",
];
и не думать о том, надо ли после очередного элемента ставить запятую или нет.
Разница в том, что у функции должно быть возвращаемое значение, и должен быть способ вернуть Unit (который ()
), причём без особых приседаний. Я думаю, вы бы первый возмутились, если бы все возвращающие Unit функции пришлось бы завершать конструкцией ()
только чтобы они не вернули чего-то ещё.
Поэтому последняя точка с запятой получается естественным критерием необходимости возвращать из функции значение.
А с литералами массива такой проблемы нет.
Конечно, я тут придираюсь к мелочи, казалось бы. Но дело в том, что когда с такой неконсистентностью постоянно сталкиваешься, неприятно.
Добавлю еще, например, не всегда допускаемый автовывод типов. В одних местах он есть, в других — нельзя.
В общем мне надоело. По теме же — Стоит предпочесть что угодно вместо С, только не Раст.
Ну, в Си или С++ вы тоже можете написать void foo() { return 42; }
и это, если я правильно помню, тоже будет ошибкой. В Rust всё так же, только без return
В Rust нет "процедур", то есть функций, которые не возвращают значения. Когда такая нужна — пишется функция, которая возвращает значение типа "пусто". Но пустое значение при этом она-таки должна вернуть! Вот пример:
fn foo() {
}
Это то же самое, что
fn foo() -> () {
()
}
Или
fn foo() -> () {
return ();
}
Или
fn foo() -> () {
;
}
Так что вы можете написать:
fn foo() {
statementA;
statementB;
}
Что на самом деле будет означать:
fn foo() -> () {
statementA;
statementB;
()
}
В данном примере мы видим, что перестановка двух строк вызовов потенциально чистых функций без побочных эффектов, ломает Раст программу.
Собственно, это и приводит меня в когнитивный диссонанс.
Я считаю это бредом, идиотизмом, и что такого не должно быть ни в одном ЯП.
(Хотя и понимаю, что это просто потому, что разработчики Раста хотели помочь, но недопомогли ввиду тупого ОБ-чекера, который понимает только один из двух вариантов)
раст == детский сад?
Про двусвязнай список — это отсылка к частому упреку в сторону Rust, что, дескать, на языке с GC можно запросто список соорудить, на C/C++ — тоже — просто возьми указатели, а на Rust не так-то просто: нужно думать о слабых умных указателях, дабы избежать циклических ссылок. Ну либо также, как в C/C++, но с unsafe — и где тогда ваша хваленая безопасность в Rust?
Тут аргумент автора статьи вполне верный. Он о том, что примитивный двусвязный список на указателях — это совсем не безопасная вещь, и закономерно, что в языке, ориентированном на автоматический отлов небезопасных конструкций (включая потоконебезопасные), соорудить безопасный список — не такая тривиальная задача.
При переходе с C++ придется расстаться с ООП (в виде наследования), со всякой нетипизируемой шаблонной магией, с самоссылающимися структурами данных да и вообще с алиасингом указателей на изменяемый объект, также придется привыкать программировать без исключений и UB :)
Rust — потому что гирибы порядка ржавчинные, их очень трудно убить ) Ну и к железу это понятие довольно близко.
примитивный двусвязный список на указателях — это совсем не безопасная вещьЭто каким таким образом?
Можно где-нибудь "потерять" ссылку на освобождённой элемент. Вроде бы банальная проблема из серии "не делайте так", в смысле "не оставляйте ссылки на элементы где попало" — вот только оставлять их придётся, иначе не видать удаления за O(1).
Можно случайно добавить элемент в два списка сразу. Можно освободить список не очистив его. Можно освободить элемент списка не удалив его их списка...
Для того, чтобы находить такие ошибки нужен другой настрой мыслей. Как у хакеров: "Как это можно использовать не по назначению?". Так что не удивительно, что не сталкивались.
Ну здрасьте! Все перечисленные мною ошибки легче всего допустить как раз в Си. Плюсы ограждают от части из них, Rust — от ещё большей части
В один список тоже можно добавить элемент два раза, и это будет такой же ошибкой как и добавление в два разных списка.
Отчасти вы правы. Поэтому я для себя сформулировал такое правило: "Rust упрощает сложное, но усложняет простое". Если вы пишите скрипт в пару строк — Rust для этой цели будет не лучшим выбором. А вот если ваше ПО достаточно сложное или предполагается его усложнение в будущем — то тут издержки Раста со временем окупятся сполна.
В точку. Играться на расте в олимпиадное программирование вообще не вариант.
А вот не факт, кстати. Я игрался в олимпиадное программирование на плюсах, и мы поймали на финале ACM ICPC столько "ошибок компилятора" (которые лет через пять оказались нашими ошибками), что лучше бы я писал на Расте. Результат бы, наверное, всё же не улучшился — но вот нервов бы мы потратили определённо меньше.
А в Rust придется платить сложностью реализации за ситуации, которые никогда не возникнут.
Но платить никто не заставляет. Лишь указывают на наличие проблем.
Но ведь односвязный список, если я правильно понимаю, в Rust реализуется.
И тогда получается странное:
двусвязный список реализовать нельзя, потому, что он опасен, тем что проблемы№1,2,3…
в односвязном списке те же проблемы №1,2,3… но его реализовать можно.
Эти доводы — против простого списка на Си. И да, против односвязного списка они тоже работают.
Двусвязный список на safe Rust реализовать нельзя не из-за этих доводов, а потому что там указатели никак не связаны с отношением владения.
ПС
А что позволит Rust «запретить» утечку памяти, связанную с забыванием удаления элемента при самописаном листе и сигнатуре:
fn extract_from_list(list: tlist, key: int8) -> &mut tlist
//извините не знаю Rust, но смысл в том, что удаляемый элемент будем возвращать из ф-ии по ссылке и тогда автоудаления при выходе из scoupe не произойдёт.
Разумеется, этих проблем в safe rust возникнуть не может, на то он и safe (кроме двойного добавления элемента в интрузивный список).
извините не знаю Rust, но смысл в том, что удаляемый элемент будем возвращать из ф-ии по ссылке и тогда автоудаления при выходе из scoupe не произойдёт.
Лучше его, напротив, возвращать по значению (не забыв удалить его, разумеется), тогда и автоудаление не понадобится.
fn extract_from_list(list: tlist, key: int8) -> &mut tlist
Такое в Rust не прокатит, так как тут list
перемещен в функцию, а не передан по ссылке, а это значит, что компилятор потребует явно указать лайфтайм для ссылки в возвращаемом значении:
fn extract_from_list<'a>(list: tlist, key: int8) -> &'a mut tlist
Но чему будет равно время жизни 'a
? Значение list
явно будет жить меньше, чем 'a
, так как оно пропадает при завершении работы функции. Поэтому в таком случае компилятор выдаст ошибку о невозможности вернуть ссылку на объект, которым владеет функция.
Выглядит так, что Rust не какая-нибудь «заумная фигня», а язык, решающий те же актуальные вопросы, что и С++ за zero-cost (разумеется несколько по-другому, это же не D / C++++, а другой язык).
С одной стороны интересно, с другой стороны размер rust book говорит, что на rust надо будет десятки часов потратить…
Подскажите ещё момент: а в Rust для трэйтов есть имплементации-функций по умолчанию (как в Haskell для тайпклассов)?
а в Rust для трэйтов есть имплементации-функций по умолчанию
есть https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations
Если нужно перегрузки дженериков делать, по типу с++, то есть ещё такое
https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md но это ещё unstable наверное.
тут list перемещен в функцию, а не передан по ссылкеи потому эта функция не имеет смысла.
Должно быть
type Tlist = Vec<i32>;
fn extract_from_list(list: &Tlist, key: usize) -> Tlist {
let mut nl = Tlist::new();
nl.push(list[key]); // may panic!
nl
}
Ммм, погодите-ка. Шаблонная магия в некотором смысле вся «нетипизируемая», если так можно выразиться, до момента передачи типа в шаблон, после чего всё становится строго типизированным. Шаблон должен отвечать используемому интерфейсу, — да, иначе не соберётся. Т.е повторюсь, все шаблоны как бы одновременно «нетипизируемые», но результат всё равно строго типизирован. По сути, шаблоны это такие «макросы на стероидах» (с).
Т.е. я правильно понял, в расте вообще нет шаблонов?
В плюсах шаблоны, как вы и написали, типизируются только после подстановки конкретных типов, а в Rust — до таковой подстановки. Поэтому в Rust "шаблоны" (которые дженерики) беднее, но зато могут быть проверены компилятором и для них нормально работают подсказки в IDE.
Так же с небольшим количеством шаблонной магии можно проверить наличие у типа нужного дата-мембера, хоть и не так «батчем», как с интерфейсными функциями (хотя может и можно батчем, я просто не заморачивался с этим, т.к. по хорошему если выставлять наружу важные дата-мемберы, лучше сделать это через геттер, который будет описан в интерфейсном классе).
Вполне допускаю, что это лишь некий эрзац-заменитель того, что может раст, ибо имея желание, можно отыскать дырки, однако для практического использования этого достаточно, чтобы избежать случайно-неслучайной подстановки неверного типа.
там еще и язык макросов отдельный, чтобы медом не казалось
А в чём вы видите проблему при этом?
> Ударит ли вас компилятор по рукам, если вы концептами, SFINAE или ещё как потребуете только operator< или operator>?
Ээээ… Что значит «ударит ли по рукам» если код не скомпилится?
А в чём вы видите проблему при этом?
В том, что непонятно при использовании функции, связана ли проблема с тем, что функция неправильно вызвана, или с тем, что сама обобщённая шаблонная функция криво написана.
Ну, хотя, конечно, если бы компилер тут помогал, это было б здорово, да. Писал уже об этом в другом коменте.
Документация устаревает, документация врёт, документация не проверяется компилятором. Типы как-то надёжнее будут.
Я именно так делал в своей нс-либине. Там весьма жЫрная кодбаза, создававшаяся на протяжении нескольких лет и без этого её было бы просто невозможно соркестрировать так, чтобы можно было бы не бояться вносить изменения в код. А со статическими интерфейсами (подчёркиваю, статическими, это важно, не обычными виртуальными) это совершенно не проблема. Причём это, в основном, не «закат Солнца вручную» — описывать требования к типу всё равно надо и это львиная доля забот. Проверки — да, это оверхед и это должен бы делать компилер, но это мизер всех забот.
Верно, но если вы работаете только через пред.описанный интерфейс и проверяете тип на соответствие ему
Вот только никто за вас не проверит, точно ли вы работаете через этот интерфейс, или случайно задействовали что-то недокументированное.
Если очень надо — тоже можно решить через враппер, автоматически создаваемый при определении интерфейса, но навскидку пока красивой реализации этого не вижу. Да, повторюсь, что поддержка концептов очень нужна в плюсах. Надеюсь, они её таки сделают.
На практике предположу, что если это выглядит проблемой — вероятно, что-то не то со стилем написания кода, в частности с его декомпозицией. Такой код скорее всего уже будет трудно понимать.
Нейросетки вполне можно описать так, чтобы их проверял компилятор.
Ну вот если я захочу за каким-нибудь надом сделать свой слой — кто проверит, что я реализовал все нужные методы?
Это всё, конечно, замечательно, но, во-первых, дженерики в Rust работают без наследования, которое ещё нужно девиртуализировать, а во-вторых, что-то вроде Send в C++ невозможно.
Откуда взялась девиртуализация? Обычное статическое наследование, никаких виртуалов…
> что-то вроде Send в C++ невозможно.
Честно пытался понять, что это, перечитывал несколько раз, но описание назначения и функционала оказалось за гранью моих скромных телепатических способностей.
ps: пока ещё все виденные мной утверждения «В С++ это сделать невозможно» на проверку следовало читать «я не знаю, как это сделать». Как обычно, не настаиваю, что сейчас именно такой случай.
Честно пытался понять, что это, перечитывал несколько раз, но описание назначения и функционала оказалось за гранью моих скромных телепатических способностей.
ps: пока ещё все виденные мной утверждения «В С++ это сделать невозможно» на проверку следовало читать «я не знаю, как это сделать». Как обычно, не настаиваю, что сейчас именно такой случай.
Send
— это маркер для типов, которые можно безопасно пересылать из одного потока в другой. Это unsafe трейт, т. е. его можно реализовать вручную, но это требует unsafe кода. В силу того, что это авто-трейт, этот трейт реализуется для типа автоматически, если его реализуют все составляющие типа. Это работает со всеми типами, в том числе и с анонимными сгенерированными типами замыканий. В качестве примера использования можно привести std::thread::spawn, функция для, собственно, запуска потоков. В качестве аргумента она принимает некую функцию, которую можно вызвать без аргументов и получить значение некоторого типа. На тип функции и на тип возвращаемого ею значения наложено ограничение Send
, поэтому её нельзя вызвать с типами, значения которых небезопасно пересылать между потоками (как Rc, например, который является потоконебезопасным указателем со счётчиком ссылок).
А как вы будете выводить наличие этого самого трейта, и, главное, что наличие трейта корректно? Напоминаю, что в Rust это работает с любыми типами, в том числе с замыканиями.
- Нет уверенности, что нигде в коде не спрятался вызов необёрнутой функции.
- Невозможно проверить на Send локальные переменные сопрограмм.
cppcoro::task foo() {
bar bar;
co_await 10s;
bar.baz(); // UB если bar - не Send
}
Да, я имел ввиду, что далеко не все, что могут делать шаблоны можно "типизировать", то есть выразить в системе типов (Раста). Кое что с помощью обобщенных типов нельзя сделать, что можно с помощью шаблонов.
Пусть для определённости будет Control Flow Graph IR программы (сильно разряженный, направленный граф с циклами, без необходимости многопоточного использования)?
Всё-таки структуры данный с циклическими ссылками вещь весьма частая в использовании и весьма эффективная при реализации «в лоб» на указателях.
Если делать граф на safe Rust — то у самого графа должен быть список Rc на вершины, у вершин — Rc на дуги, а у дуг — Weak на вершины. В отличии от двусвязного списка, тут можно построить иерархию владения, а потому подобное представление возможно, пусть и не вполне оптимально.
— прямая дуга, та которая next — владение (или Rc ссылка) на элемент.
— обратная дуга, та которая prev — Weak ссылка?
П
Потому что двусвязный список — слишком примитивная структура данных, чтобы делать её настолько неоптимально.
А на сколько это будет неоптимально, в сравнении с условным C, разумеется примерно, в 1.5 \ 3 \ 10 раз?
(я просто сначала подумал, что варианта «немного неоптимально» нет, и есть только вариант «через unsafe»)
Впрочем, рекомендую почитать исходники стандартной библиотеки — в справочнике везде можно щелкнуть на src в правом углу.
Для однопоточного варианта.
3 слова для подсчета сильных и слабых ссылок и borrow flag на каждую ноду. Неатомарный inc при создании указателя на ноду. Неатомарный dec и ветвление при удалении указателя на ноду. Неатомарное чтение borrow flag и ветвление при чтении данных ноды. Дополнительно неатомарная запись в borrow flag при начале и конце записи данных ноды.
Многопоточный двусвязный список не рассматриваю — это сложно везде.
А вы когда учебник по квантовой физике читаете, тоже на авторитет не полагаетесь? или, скажем, ПДД...
Если для вас квантовая физика — это какая-то магия, в которой нужно полагаться на авторитет, то вам наука явно не подходит как род занятий.
А вы все выкладки и все эксперименты сами повторяете и воспроизводите?
Конечно приходится полагаться на авторитет. Точно так же как мы верим создателям компиляторов, библиотек и ОС. Ибо знать все не возможно, какие-то базовые штуки университетской программы, которые разжеваны из года в год поколениями студентов на лекция и лабах — это одно. А проверять все выкладки в статьях которые я просматриваю в большом количестве, или повторять эксперименты, которые можно сделать в лучшем случае в двух лабораториях мира — это другое. Бывают печальные случаи что через 5, 10 или 15 лет находят ошибки в научных статьях. Естественно ровно одному человеку на слово не верят, т.к. он может сделать ошибку по невнимательности или по злому умыслу. Но в целом научному сообществу и процессу приходится верить. Плюс большинство вещей проверяются косвенно. Типа использовали метод Х и получили то что надо => метод Х вроде как правильный. Но когда в учебнике написано что таким-то чуваком был сделан такой-то эксперимент, то приходится верить учебнику. А вот новостным помойкам я не верю, они свой авторитет не заслужили.
В 99,99% случаев никто в здравом уме и трезвой памяти не станет решать типовые и давно решенные задачи, когда уже есть множество готовых проверенных решений в виде STL, Boost, Poco и да-тысячи-их-на-любую-потребность.
Сколько в мире C++ реализаций стандартных библиотек и компиляторов?
это с какими полезными и удобными возможностями С++ придётся неминуемо расстаться при переходе на раст.
Вычисления времени компиляции? В конце концов RFC2000 всё ещё пилят. С другой стороны у нас есть макросы и процедурные макросы. Через последние можно делать практически что угодно (да хоть дум на найтмаре запускайте). От классического ООП придётся отказаться (хотя не уверен, что это плохо). Попробуйте дополнить список, мне действительно интересно, что же такого я не могу делать в Rust, но смог бы в C++.
А нафига они нужны с тайпчеком после раскрытия?
Например новые типы определять во время компиляции. Как сделать функцию конкатенации std::array на расте?
А в чём, собственно, проблема?
Примерно так. Шаблоны не требуются.
В исходнике массив копируется через core::ptr::write (memcpy)
А если там в массиве Т — владеющие объекты, то при дропе получим двойное освобождение?
Опять забыли прочитать документацию? ptr::write мувает src в dst.
А если там в массиве Т — владеющие объекты, то при дропе получим двойное освобождение?
С чего бы? ptr::write
принимает второй параметр по значению же. Это автоматически означает, что деструктор для него вызываться не будет.
В идеале нужны какие-то const generics, только не те что предлагают стабилизировать на следующем этапе. Язык трейтов он конечно тьюринг полный, только очень стрёмный, поэтому интересно уметь поднимать const fn на уровень типов.
В идеале это, конечно, хотелось бы на const generics написать, но там тогда получается что-то вроде fn concat_arrays<T, const N: usize, const M: usize>(a: [T; N], b: [T; M]) -> [T; N + M]
, т. е. значение, зависящее от обобщённого параметра, что компилятор Rust пока что совершенно не умеет обрабатывать.
У них задача легче, т.к. M и N на момент раскрытия шаблона известны.
это с какими полезными и удобными возможностями С++ придётся неминуемо расстаться при переходе на раст.
ну и ответ что всё таки не только job security.
Я тоже не спорю.
Кстати вот годный коммент на эту тему https://github.com/rust-lang/rust/issues/68436#issuecomment-663190861
И при этом почти полное отсутствие библиотек, и фактическое отсутствие менеджера зависимостей...
А на каком этапе они проверяются? Т.е. например есть такое
template<int x, typename F>
void g(F f) {
static_assert(f(x) > 0, "ducks");
}
int main() {
g<3>([](int x) { return 2 * x; });
}
Кроме нормальной типизации что F это функция как много надо ещё будет доказывать?
Согласен. Отсутствие встроенных концептов и проверки соответствия определения типа концепту — не есть гуд. Если надо, думаю, это можно решить (не интересовался), но да, будет классический «закат Солнца вручную» когда это мог бы делать компилер.
> компилятор не проверяет, что те проверки, которые вы добавили в её сигнатуру, на самом деле соответствуют её телу
Ммм, тут Вашу мысль не понял. Проверки по смыслу делаются по внешним признакам, бо duck typing. Если прям очень надо верифицировать тело-поведение, думаю, это тоже в каком-то видер решаемо тэгированием и доп. орг. мерами с разумной пресуппозицией, что пользователь/программер вредить не будет.
Если прям очень надо верифицировать тело-поведение, думаю, это тоже в каком-то видер решаемо тэгированием и доп. орг. мерами с разумной пресуппозицией, что пользователь/программер вредить не будет.
На следующий день после введения таких мер у вас программист ошибётся и поставит не тот тег, который надо. И не потому, что он такой зловредный, а потому, что это обычный человек с весьма ограниченным ресурсом внимания.
назойливый двусвязный список, который бывает невозможно выразить в безопасном RustВ безопасном тоже можно сделать используя Rc<RefCell> или Arc<Mutex> в зависимости от задачи.
Но зачем, ведь есть реализация списка в стандартной библиотеке.
В результате может получиться так, что разработчика под Rust будет тяжело найти, а это может дать такую отрицательную обратную связь, что он может вообще «не взлететь».
А иначе — минус от обиды за судьбу Rustа?
Хабр не дает менять оценку, поэтому теперь у вашего комментария плюс чисто из-за гонок данных: я поставил просто чтобы убрать минус, но и кто-то еще успел поставить — суммарно получился плюс, о котором я узнал постфактум.
Не знаю, кто все время минусует нормальные комментарии, но я уже устаю компенсировать минуса (ведь скажут, что это растоманы такие вредные, всех минусуют, кто с ними не согласен).
(ведь скажут, что это растоманы такие вредные, всех минусуют, кто с ними не согласен)
Но ведь минус как раз знаком неаргументированного несогласия и является. Что постыдного в минусе в случае взаимного несогласия?
Минус не очень хорошая штука — он влияет на рейтинг и на видимость комментария. Я обычно просто несогласие выражаю в комментарии, либо плюсую ответы тех, с кем согласен. Минусую только те, где явно есть провокации, деструктивные попытки съехать на личности, оскорбления и пр. То есть я воспринимаю минус как "убрать этот комментарий", а не как "выражаю несогласение с точкой зрения автора". К сожалению система оценок Хабра и ее влияние принуждают к этом.
Сомневаюсь, что Rust претендует только на обозначенную вами нишу. Rust хорошо себя показывает и в тех областях, где традиционно используется какая-нибудь Java. Rust — универсальный язык общего назначения, его место — где-то посередине между C и Haskell, так что расползается он в обе стороны.
Rust хорошо себя показывает и в тех областях, где традиционно используется какая-нибудь JavaТак почти любой развитой ЯП хорошо покажет себя в задачах Джавы (не в упрек последней). Но там ведь основной вопрос — это баланс надежности, простоты и эффективности. А от очень многих я слышу, что Rust им непривычен / труден / странен. Конечно, любой Тьюринг-полный язык можно применить в любой области, особенно если этот язык нравится ) Обычно, кстати, так и делают — изобретают сначала ЯП для одних целей, а потом стараются максимально расширить область его применения. Здесь все ок. Просто решать вопрос о применении языка будут не разработчики Раста, а бизнес, а там эмоции одни — деньги, деньги, деньги.
Вообщем, будем посмотреть. И хотелось бы, хоть я и не растоман, чтобы он все-таки «взлетел» по-настоящему.
Ну Rust все же императивный язык, так что он намного более привычен для условного джависта, чем тот же Haskell. Плюсы Раста — это возможность глубоко оптимизировать программу, если это понадобится, и сильно больший контроль типов со стороны компилятора. Ради этих плюсов вполне можно потерпеть полуручное управление памятью, которое для серьезного проекта совсем не препятствие.
Конечно Rust будет более привычен многим, чем Haskell, но ведь ему надо переманивать джавистов, плюсовиков и сишников — при этом находясь в их же предметной области. Это нелегко (но и не невозможно).
А вот в сфере медицинского оборудования, управления транспортом и других критических к надежности сферах Rust будет очень полезен.
Предпочитайте Rust вместо C/C++ для нового кода