LINUX.ORG.RU

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

Так и Qt, boost и даже abseil - тоже не std

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

Я (как и ты) для себя на этот вопрос давно ответил

Я — недавно.

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

тесты, которые находятся прямо в сорцах

Как будто что-то плохое

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

byko3y ★★★★
() автор топика

пятница, 13-я. в смысле страница обсуждения.

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

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

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

Если у тебя функция должна вернуть trait, но вернула что-то не то, тогда возникнет ошибка при использовании этого «чего-то не того».

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

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

А ты хаскель-то выучил до уровня «как родной»? (:

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

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

Шаблоны — это просто название для механизма обобщенного программирования в C++.

А дженерики - название для механизма обобщенного программирования в расте (ну и не только). И?

impl AsRef — 4 штуки

Не имеет отношения к «безопасности указателей». AsRef<str> for String можно воспринимать как operator std::basic_string_view в С++: преобразование из владеющей строки в слайс. У остальных реализаций похожий смысл.

impl Borrow impl BorrowMut

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

impl Clone

Копирующий конструктор.

impl FromIter - 7 штук

Конструктор из итератора.

impl FromStr

Для обобщённого кода (и консистентности).

Итаво — 45. Больше половины. Можно говорить о том, что какие-то функции From* и Index* имеют функциональное, а не формальное назначение, потому я согласен на ровно половину.

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

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

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

Да, я считаю тесты частью проекта. Если они написаны на каком-то другом языке - ну ОК, тогда это проект написанный на нескольких языках.

Мое эмпирическое правило по прежнему не опровергнуто — примерно на десятках тысяч строк в проектах на расте неизбежно начинаются unsafe-ы.

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

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

Да, я считаю тесты частью проекта. Если они написаны на каком-то другом языке - ну ОК, тогда это проект написанный на нескольких языках

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

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

Да, тебе нужно будет приложить много усилий, потому что доля таких проектов-исключений мала (если они вообще существуют). Что я и имею в виду под подтверждением моих слов. А вот по поводу «проект бесполезен» я пока что даже не намекал.

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

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

Ну там мораль немного другая: компилятор не обязательно выведет то, что ты задумал

Мораль такая: компилятор выводит время жизни по крайне примитивным правилам, которые не работают для любой мало-мальски сложной функции.

Если у тебя функция должна вернуть trait, но вернула что-то не то, тогда возникнет ошибка при использовании этого «чего-то не того»

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

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

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

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

https://i.stack.imgur.com/JIH2B.png

Наверное, единственное новое понимание, которое у меня появилось по мере углубления — это что каждая третья либа/фреймворк для JS решает одну и ту же проблему: как на базе ЯП, имеющего изменяемый по месту ассоциативный массив в качестве единственного сложного типа данных, сделать сложную систему так, чтобы от одной строчки кривого кода, по ошибке изменяющего глобальный объект, SPA не превращалось в тыкву с беспорядочно расположенными блоками до перезагрузки страницы. React, Vue, Angular, TypeScript — самые известные примеры подходов к снаряду. Привет расту, кстати.

Шаблоны — это просто название для механизма обобщенного программирования в C++.

А дженерики - название для механизма обобщенного программирования в расте (ну и не только). И?

И это одно и то же с незначительными отличиями.

impl AsRef — 4 штуки

Не имеет отношения к «безопасности указателей». AsRef<str> for String можно воспринимать как operator std::basic_string_view в С++: преобразование из владеющей строки в слайс. У остальных реализаций похожий смысл

В C++ это такая же вспомогательная формальная конструкция.

impl Borrow impl BorrowMut

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

И почему же нельзя сразу вызвать методы для слайсов?

impl FromIter - 7 штук

Конструктор из итератора

Не из итераторА, а из итераторОВ. Которых почему-то целый вагон.

impl FromStr

Для обобщённого кода (и консистентности).

Clone и From<string> пересекаются, и FromStr пересекается с From<str> — такая вот целостность.

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

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

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

нет, у раста с графикой только зайчатки, все руками, оставайтесь на джаве, для такого «продакшена» он еще не «рейди»

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

Где-то к середине статьи у меня сгорел мозг. Я не подозревал, что всё настолько плохо. То есть, компилятор может вывести время жизни для чистых функций в пару строчек, но ни на что сложнее он уже не способен.

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

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

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

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

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

Пишет фанат раста.

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

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

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

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

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

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

B Go дженрики уже почти завезли.

И это одно и то же с незначительными отличиями.

Не уверен. Этак и сишные шаблоны можно можно объявить средством обобщённого программирования с «небольшими отличиями».

В C++ это такая же вспомогательная формальная конструкция.

Не понял мысль.

И почему же нельзя сразу вызвать методы для слайсов?

Почему нельзя вызывать методы A на объекте типа B? Потому что это разные объекты (в том числе, не связанные отношением наследования).

Не из итераторА, а из итераторОВ. Которых почему-то целый вагон.

Шесть итераторов, да:

  • Строки.
  • Слайсы.
  • Box (грубо говоря, «владеющий слайс»).
  • Символы.
  • Ссылки на символы.
  • Cow.

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

Clone и From пересекаются

Пересекаются, да. Но смысл-то разный. Первое - это «дай мне копию объекта», второе - «преобразуй A в Б». И второе как раз позволяет писать меньше: вместо двух перегрузок from_A и from_Б можно иметь одну функцию, если типы уже умеют преобразовываться друг в друга.

FromStr пересекается с From

Нет же, совсем разный смысл. From - это гарантированное преобразование, а второе - это «попробуй распарсить из строки» (let: u8 = "123qwe".parse()).

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

Да вот как раз не думаю, что в системном/низкоуровневом языке можно обойтись без этих формальностей. Они могут существовать «в уме» (как лайфтайм у плюсовых спанов), но они есть. И если у нас есть символы, последовательности символов, слайсы/спаны, владеющие строки, то куда от этого деваться? Это всё не бесполезные сущности.

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

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

B Go дженрики уже почти завезли

Уже который год «почти».

Этак и сишные шаблоны можно можно объявить средством обобщённого программирования с «небольшими отличиями»

Разве это не так? В большинстве случаев сишные макросы применяют для типонезависимых описаний.

В C++ это такая же вспомогательная формальная конструкция.

Не понял мысль

string_view слизано с раста, его не было в C++ очень долго, и этот класс создает огромный потенциал для use-after-free в C++, потому-то его и не было в крестах. Это чужеродная конструкция для крестов, короче говоря.

И почему же нельзя сразу вызвать методы для слайсов?

Почему нельзя вызывать методы A на объекте типа B? Потому что это разные объекты (в том числе, не связанные отношением наследования)

Эту проблему решает в том числе Go, который позволяет делегировать методы к содержащимся объектам.

Строки.
Слайсы.
Box (грубо говоря, «владеющий слайс»).
Символы.
Ссылки на символы.
Cow

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

Ну и где ты увидел в крестах конструкторы из коробки/коровы (Box/Cow)? Они тоже являются итераторами? Так-то я могу придумать еще десяток контейнеров, и создатели языка как бы подразумевают, что я должен буду так же реализовать под них трейты FromIterator для строк.

Clone и From пересекаются

Пересекаются, да. Но смысл-то разный. Первое - это «дай мне копию объекта», второе - «преобразуй A в Б». И второе как раз позволяет писать меньше: вместо двух перегрузок from_A и from_Б можно иметь одну функцию, если типы уже умеют преобразовываться друг в друга

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

impl From<String> for Vec<u8, Global>
impl From<String> for Arc<str>
impl From<String> for Box<str, Global>
impl From<String> for Box<dyn Error + Send + Sync>
impl From<String> for Box<dyn Error>
impl From<String> for OsString
impl From<String> for PathBuf
impl From<char> for String

Расскажи мне, как это не относится к модели ручного ковыряния вилкой в модели Rust. Я бы сказал «да, прикольная реализация множественной диспетчеризации/полиморфизма», однако же, в данном случае полиморфизм происходит по служебным структурам, а не по различным типам данных. А реализация множественного полиморфизма через специальные функции «все со всеми» имеет сложность O(N^k), где k — число переменных типов в одной функции. То есть, улететь туземун с таким подходом очень просто.

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

Да вот как раз не думаю, что в системном/низкоуровневом языке можно обойтись без этих формальностей. Они могут существовать «в уме» (как лайфтайм у плюсовых спанов), но они есть. И если у нас есть символы, последовательности символов, слайсы/спаны, владеющие строки, то куда от этого деваться? Это всё не бесполезные сущности

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

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

Rust, вроде бы, начал путь с неизменяемых данных и передачи владения, а получилось как всегда — крайне переусложненная модель данных аля C++. Хочется задать вопрос создателям: почему нельзя было вообще всю работу с «mut» сделать «unsafe»? Она ведь в любом случае очень быстро становится unsafe, потому что даже имеющихся в расте переусложенных инструментов очень быстро начинает на хватать. Я могу даже попытаться дать ответ на этот вопрос: потому что создатели Rust были прожженныи крестовиками, и грили «зочем нам этот недоязык? Сделайте нам ХОРОШО, КАК В КРЕСТАХ».

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

Пять проблем C++ решили — пять новых проблем создали в собственном языке, и двести проблем из C++ скопировали без изменения. И теперь вот это вот подается чуть ли не как как революция.

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

Уже который год «почти».

Ты действительно хочешь сказать, что дженериков в Go в принципе не будет?

Можем поспорить, что через год они точно появятся. Вон в С++ про модули тоже долго говорили, но сделали же.

Разве это не так? В большинстве случаев сишные макросы применяют для типонезависимых описаний.

Применяют, да. Потому что ничего лучше нет. Не согласен именно с тем, что отличия минимальные.

string_view слизано с раста, его не было в C++ очень долго, и этот класс создает огромный потенциал для use-after-free в C++

Так-то любая ссылка создаёт потенциал для use-after-free, в С++ с этим привыкли жить. Проверять когда там string_view появилось в бусте лень, так что может ты и прав, что вдохновились растом, но не уверен.

Эту проблему решает в том числе Go, который позволяет делегировать методы к содержащимся объектам.

Но в расте слайс не содержится в строке.

Ну и где ты увидел в крестах конструкторы из коробки/коровы (Box/Cow)?

Вообще без этого вполне можно было бы жить: это не конструктор из Box, это конструктор из итератора, который возвращает строки (в том или ином виде). В С++ ты можешь инициализировать строку итератором по символам, а инициализацию из итератора по строкам уже придётся самому писать (возможно через какой-нибудь std::accumulate). В расте просто решили дать больше удобства.

CoW в С++ в стд действительно нет.

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

Контейнеров вроде вектора? Тогда нет, ничего реализовывать не нужно. Или вроде бокса? Ну тогда да.

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

Не согласен, что (все или даже половина) эти типы данных являются служебными:

  • String - изменяемая utf8 строка.
  • Box<str> - неизменяемая utf8 строка.
  • OsString - не обязательно utf8 строка.
  • PathBuf - путь со своими инвариантами.
  • Box<dyn Error> - ошибка, которая может быть представлена строкой.

Из прям растовых особенностей тут разве что вторая реализация для dyn Error (и то это скорее «легаси» проблема) и Arc.

Хочется задать вопрос создателям: почему нельзя было вообще всю работу с «mut» сделать «unsafe»? Она ведь в любом случае очень быстро становится unsafe, потому что даже имеющихся в расте переусложенных инструментов очень быстро начинает на хватать.

Тут я не согласен.

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

Ты действительно хочешь сказать, что дженериков в Go в принципе не будет?

Будут, только не совсем ясно в каком виде. Вроде уже приняли решение о внесении обобщений в виде ограничений типов — это, однако, заметно проще механизм, чем в C++/Rust, и ближе к механизму утиной типизации интерфейсов, который в Go был всегда.

Так-то любая ссылка создаёт потенциал для use-after-free, в С++ с этим привыкли жить. Проверять когда там string_view появилось в бусте лень, так что может ты и прав, что вдохновились растом, но не уверен

«Based on the StringRef implementation in LLVM (http://llvm.org) and N3422 by Jeffrey Yasskin
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html"

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

Эту проблему решает в том числе Go, который позволяет делегировать методы к содержащимся объектам.

Но в расте слайс не содержится в строке

А что же в ней содержится? Дерево символов? Связанный список блоков символов?

В С++ ты можешь инициализировать строку итератором по символам, а инициализацию из итератора по строкам уже придётся самому писать (возможно через какой-нибудь std::accumulate). В расте просто решили дать больше удобства.

CoW в С++ в стд действительно нет

В C++ ты, как правило, будешь управлять памятью руками, и потому оборачивать в Cow/Box возникает меньше необходимости.

Box<str> - неизменяемая utf8 строка
OsString - не обязательно utf8 строка
PathBuf - путь со своими инвариантами

Что характерно, почему-то в C++ все кодировки обслуживаются одним типом строки.

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

Что характерно, почему-то в C++ все кодировки обслуживаются одним типом строки.

Это аргумент не в пользу C++, даже если игнорировать факт, что там кроме string есть wstring, u16string, u32string. Как я понимаю, в Rust разновидности строк разрабатывали практики, которые столкнулись с реальными проблемами, например, с открытием файлов в разных ОС, имена которых содержали не английские буквы. В C++ разновидности строк делали теоретики, использующие в жизни только один язык. Поэтому многие C++ разработчики предпочитают строки Qt или других фреймворков.

Kogrom
()

По теме ещё не разобраны следующие вопросы.

  1. Rust vs Zig. Понятно, почему фанаты избегают этот вопрос. Но возможно, DarkEld3r что-то сможет сказать, как самый адекватный сторонник Раста.
  2. Чем плохи исключения? Исключения плохи только в системном программировании или в прикладном тоже? Можно понять, что исключения могут создать проблему, если мы библиотеку, созданную на одном языке, пытаемся использовать в другом. Но если мы находимся в рамках одного языка, то тут вроде бы одни преимущества.
Kogrom
()
Ответ на: комментарий от byko3y

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

Компилятор в этом случае ничего не выводит. Lifetime elision - это сокращение записи сигнатуры функции для некоторых часто встречающихся случаев.

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

Компилятор в этом случае ничего не выводит. Lifetime elision - это сокращение записи сигнатуры функции для некоторых часто встречающихся случаев

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

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

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

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

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

Из-за отсутствия разделения обработки разнородных ошибок, попытки перенести контракт функции из документации в её сигнатуру приводят к проблемным вещам вроде checked exceptions в Java: функция тупо собирает все типы исключений из дерева вызовов.

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

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

комитет проголосовал за P2186R2 Я уже добавил в таблицу на cppreference. Как примут этот патч в драфт стандарта, так и в pdf версии стандарта про это будет написано.

Разработчики компиляторов/стандартных библиотек про это тоже знают: https://github.com/microsoft/STL/issues/1988

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

Будут, только не совсем ясно в каком виде. Вроде уже приняли решение о внесении обобщений в виде ограничений типов — это, однако, заметно проще механизм, чем в C++/Rust, и ближе к механизму утиной типизации интерфейсов, который в Go был всегда.

Вроде, вот в таком. Разве нет?

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

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

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

А что же в ней содержится? Дерево символов? Связанный список блоков символов?

Вектор байтов. Внутреннее представление у этих типов разное. Слайс - это указатель плюс размер, строка - это вектор (который является указателем плюс два размера).

В С++ вон тоже строка не наследуется от string_view, да и было бы несколько странно.

Что характерно, почему-то в C++ все кодировки обслуживаются одним типом строки.

Во первых, нет. Есть string, wstring, u8string, u16string и u32string (я в курсе, что это инстансы одного шаблона). Есть отдельный filesystem::path.

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

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

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

Инкапсуляция, нет? А если она не требуется, то есть публичные члены структур. Данные enum’ов всегда доступны напрямую, если что.

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

Хм. Это какие такие примеры? Я что-то не припомню ничего кроме реализаций Deref и DerefMut, которые выполняют несколько другую функцию.

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

Rust vs Zig. Понятно, почему фанаты избегают этот вопрос. Но возможно, DarkEld3r что-то сможет сказать, как самый адекватный сторонник Раста.

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

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

Чем плохи исключения?

Неявностью. И хороши тоже этим.

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

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

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

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

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

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

Точка зрения на счёт Zig понятна. Прокомментирую только это:

нет макросов

Как я понял, они там есть. Просто вместо отдельного встроенного языка для макросов используют синтаксис самого Zig + несколько дополнительных ключевых слов.

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

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

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

Если код пишет опытный разработчик, то проблем с исключениями быть не должно. Если на Расте пишет новичок, то он полезет в Internet и найдёт способ как заткнуть назойливую систему сообщений об ошибках, найдёт как перехватить паники. В результате можем получить такой же «happy path», только более зашумлённый.

Направление мысли интересное, конечно, но с реализацией что-то не то. Можно было для файла, например, вместо open() предусмотреть набор функций с обработками, типа open_or_create() и open_or_throw().

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

вместо open() предусмотреть набор функций с обработками, типа open_or_create() и open_or_throw().

Композабельность Result’ов позволяет сделать это без кучи функций.

Например, open(path).or_else(|| create(path)), open(path).unwrap() и тому подобное.

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

Про Qt зачотне. Что уважаемый Дон скажет по поводу GTK?

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

Как я понял, они там есть. Просто вместо отдельного встроенного языка для макросов используют синтаксис самого Zig + несколько дополнительных ключевых слов.

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

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

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

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

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

А было бы прикольно, если бы C++ что-то позаимствал у раста. Но, к сожалению, в расте ничего нового нет и в C++ всё было до него.

Вектор байтов. Внутреннее представление у этих типов разное. Слайс - это указатель плюс размер, строка - это вектор (который является указателем плюс два размера)

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

В С++ вон тоже строка не наследуется от string_view, да и было бы несколько странно

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

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

Писатели на тикле передают привет. А если серьезно, то это оптимизации на грани фола. Если пользоваться высокоуровневыми функциями для работы со строками, то получится медленно, жирно, и надежно, а если начинать оптимизировать, то стандартные функции быстро перестают выполнять свои функции. Особенно это касается UTF-8, который является очень неудобным форматом для работы с семантикой — я вообще слабо понимаю, какой поехавший решил, что сделать UTF-8 основным типом будет хорошей идеей, когда многие уже переходят либо на чистый UTF-32, либо на набор строковых типов разной длины, как те же C++/Delphi/CPython. Причем, как минимум в двух последних это сделано прозрачно, и при этом всегда есть возможность конвертнуться в UTF-8 для экспорта-импорта.

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

Инкапсуляция, нет? А если она не требуется, то есть публичные члены структур. Данные enum’ов всегда доступны напрямую, если что

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

А если она не требуется, то есть публичные члены структур. Данные enum’ов всегда доступны напрямую, если что

А там все равно придется руками преобразования делать, потому что «ссылка на неизменяемую ссылку на изменяемое значение» и вот это всё.

Хм. Это какие такие примеры? Я что-то не припомню ничего кроме реализаций Deref и DerefMut, которые выполняют несколько другую функцию

Давай сделаю аттракцион: я тыкаю в случайный модуль стандартной либы раста и считаю количество одно-двухстрочников. std/src/lazy.rs — 20 одно-двухстрочников, 6 функций на несколько строчек.

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

Чисто с практической точки зрения: на расте я работу нашёл, на Zig это сделать будет непросто. Хотя с точки зрения «интересности» это, конечно, не решающий аргумент

Разреши не поверить, поскольку я не поверю, что какой-то человек пошел писать на том же C++ «потому что душа велит». C++ — бесконечно кривой язык, но по нему много работы. Rust — тоже кривой язык, по нему работы сейчас меньше, чем по крестам, но в перспективе ее станет больше. И конкуренции мало, потому что есть не так много есть желающих читать вырвиглазный ад на C++/Rust. Потому тобой движет коммерческий расчет, а потом уже от него пляшет симпатия к тому или иному ЯП.

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

«А какая же в этом тракторе шморгалка?»

https://www.youtube.com/watch?v=DAPiE-lpQ-w

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

Но, к сожалению, в расте ничего нового нет и в C++ всё было до него.

Лайфтамов всё-таки в плюсах нет. Впрочем, не вижу проблемы в заимствовании. Важно не только, что сделано, но и «как сделано» (и что не сделано).

Кого колышет внутреннее представление?

Так ведь ты выше утверждал:

Эту проблему решает в том числе Go, который позволяет делегировать методы к содержащимся объектам.

Но нет, объекты никак не связаны: ни наследованием ни композицией.

А Rust сделан с нуля, но скопировал все шрамы крестов.

Ну если ты так говоришь. (:

многие уже переходят либо на чистый UTF-32

Да ладно? Честно говоря, делфи мало интересен. В Go utf8 строки, в свифте перешли недавно перешли от UTF-16 к UTF-8.

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

Давай сделаю аттракцион: я тыкаю в случайный модуль стандартной либы раста и считаю количество одно-двухстрочников. std/src/lazy.rs — 20 одно-двухстрочников, 6 функций на несколько строчек.

Так ведь речь шла об обилии геттеров, а не просто функций-однострочников?

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

Тут идея в том, как показать новичку оптимальный вариант действий. Сейчас он пытается открыть файл и сразу получает ошибку. Ищет как заткнуть источник сообщений, пишет open(path).unwrap() и продолжает дальше. И тут мы получаем вариант как в языках с исключениями. А лучше бы он в документации сразу видел подсказку в виде open_or_create(). Хотя согласен, что тут есть недостаток в изяществе и гибкости, ибо если подсказывать дальше, то можно дойти до какого-нибудь open_or_create_and_fill_with_default_data().

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

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

Разреши не поверить, поскольку я не поверю, что какой-то человек пошел писать на том же C++ «потому что душа велит». C++ — бесконечно кривой язык, но по нему много работы. Rust — тоже кривой язык, по нему работы сейчас меньше, чем по крестам, но в перспективе ее станет больше.

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

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

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

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

Так что нет, не угадал. Я изначально выбрал раст, затем подвернулась работа. И поменял «более рыбное» направление на более интересное. Неужели в это сложно поверить?

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

Инкапсуляция, нет? А если она не требуется, то есть публичные члены структур. Данные enum’ов всегда доступны напрямую, если что.

В Обероне можно экспортировать поля только на чтение, геттеры не нужны:

	TYPE
		Controller* = POINTER TO ABSTRACT RECORD (Controllers.Controller)
			opts-: SET; (* minus means read-only export *)
			model: Model;	(* connected iff model # NIL *)
			view: View;
			focus, singleton: Views.View;
			bVis: BOOLEAN	(* control visibility of focus/singleton border *)
		END;

	PROCEDURE (c: Controller) SetOpts* (opts: SET), NEW, EXTENSIBLE;
		VAR op: ControllerOp;
	BEGIN
		IF c.view # NIL THEN
			NEW(op); op.c := c; op.opts := opts;
			Views.Do(c.view, "#System:ChangeOptions", op)
		ELSE
			c.opts := opts
		END
	END SetOpts;
X512 ★★★★★
()
Ответ на: комментарий от Kogrom

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

В простых случаях можно и Option вместо Result.

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

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

Лайфтаймы были в плюсах с момента создания.

Скажи ещё, что ты не понял мысль. Ну замени «лайфтайм» на «явный (в смысле явно присутствующий в коде) лайфтайм».

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

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

Получение среднего тоже состоит из операций: сумма массива и деление на размер массива. И в этом случае деление на размер массива будет более низкоуровневой операцией. Соответственно, при делении на ноль должны получить Option. А получим панику.

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

Лайфтамов всё-таки в плюсах нет. Впрочем, не вижу проблемы в заимствовании. Важно не только, что сделано, но и «как сделано» (и что не сделано)

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

Кого колышет внутреннее представление?

Так ведь ты выше утверждал:
Эту проблему решает в том числе Go, который позволяет делегировать методы к содержащимся объектам.

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

многие уже переходят либо на чистый UTF-32

Да ладно? Честно говоря, делфи мало интересен. В Go utf8 строки, в свифте перешли недавно перешли от UTF-16 к UTF-8

С чем я их и поздравляю. И какая у нас производительность разбора строк из нелатинских символов на Go? Я могу объяснить, почему UTF-8 в Go — потому что это специализированный язык для написания сервисов, которые, как правило, только делают примитивный разбор входного запроса в UTF-8 и выдают ответ в этом же UTF-8. По поводу Swift не могу прокомментировать ситуацию и мне лень раскапывать этот вопрос.

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

Давай сделаю аттракцион: я тыкаю в случайный модуль стандартной либы раста и считаю количество одно-двухстрочников. std/src/lazy.rs — 20 одно-двухстрочников, 6 функций на несколько строчек.

Так ведь речь шла об обилии геттеров, а не просто функций-однострочников?

Так это и есть в основном геттеры, которые дальше дергают get_or_try_init, get_or_init, get_unchecked_mut, get_unchecked_mut. etc.

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

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

Фаната C++ знаю ровно одного. Многие люди, освоившие C++, ни разу не рады этому языку. Вот фанатов хаскеля много. Фанатов лиспа много. Есть парочку фанатов жавы программирования на IDE. В интернетах встречал даже одиночных фанатов Ады и Кобола, но это некая особенная ступень упоротости, и в тех случаях это было таки желание продать себя, свои навыки — потом что никому его навыки не нужны, очевидно. Но в случае C++ ситуация совершенно иная — программистов с радостью берут, потому как-то особенно «продавать» себя не нужно.

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

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

Я изначально выбрал раст, затем подвернулась работа. И поменял «более рыбное» направление на более интересное. Неужели в это сложно поверить?

Ты изначально выбрал кресты. Как я писал «а потом уже от него пляшет симпатия». В остальном ты хорошо описал.

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

Соответственно, при делении на ноль должны получить Option. А получим панику.

Это смотря как делить: есть checked_div.

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

Время жизни у переменных в крестах есть точно так же, как и в расте.

Ну так-то оно везде есть, включая языки с GC. Но это ты уже придираешься.

Делегирование содержащимся структурам — это синтаксический сахар, внутри компилятор прописывает чтение вложенной структуры.

Так там нет общей вложенной структуры.

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