> Все эти обоснования выглядят как "денег нет, но вы держитесь". Об удобстве программиста там подумать явно поленились.Все перечисленные "неудобства" -- это просто "мне хотелось придраться, я придрался". Метод into_* в расте попадается не только в box'е, и глупо было бы делать исключения для box'а.
> "let mut s" - это похоже на желание получить что-то иммутабельное? Написал
> mut - получи изменяемую копию неизменяемого объекта.
Да. Чтобы не вваливаться в совсем уж академическую функциональщину привнесли возможность изменять объекты, но при этом всячески подталкивают кодеров работать с immutable объектами.
> Запрет на очевидные неявные
> преобразования типа - это не "немного вымораживает", это идиотизм.
Ну, может быть, они тут перебрали. Но если ты о запрете на неявный вызов конструкторов типов -- это абсолютно правильно. Зачем было запрещать неявно расширять i32 до i64 я не знаю. Но я верю, что причина была. Потому что в других случаях они позволяют себе пихать в отдельные типы возможности, которые невозможно повторить в собственной реализации.
> Чем String от slice отличается я понимаю. Я не понимаю панического страха
> перед неявными (но, разумеется, описанными в спецификации и совпадающими с ожиданиями
> программиста) преобразованиями типов, выделениями памяти и прочими исключениями,
> направленными на то, чтобы код легко писался и читался.
Я тоже не понимаю. Я много чего не понимаю. Квантовую механику, например, не понимаю. Но я не считаю это поводом записывать физиков в список идиотов, которые не умеют создавать теории. И тебе рекомендую поступать так же, то есть рассматривать "я не понимаю" не как свидетельство того, что кто-то другой глупец, а как свидетельство того, что у тебя недостаточно знаний или ума.
Разработчики раста никогда не отказываются рассмотреть вопросы удобства, и они всегда готовы посыпать раст ещё одним слоем синтаксического сахара. Они делают это регулярно. Просто ваши "неудобства" сводятся к тому, что "а вот в C++/D/Java/(подставьте любой другой язык) сделано иначе". Rust -- не C++/D/Java/..., rust -- это rust.
У раста есть проблемы, но они гораздо интереснее, чем неудобства связанные с необходимостью использовать & перед строкой, при вызове оператора +. Например, его borrow-checker довольно туп, и иногда запрещает делать вполне невинные вещи. Скажем, там не работает такая конструкция:
v.push(v.len());
Не работает потому, что раст, компилируя это, пытается создать одновременно в пределах одного lifetime'а два указателя на v, один из которых mutable. Но это один из фундаментальных запретов языка -- либо один mutable указатель, либо много immutable, третьего не дано -- и поэтому программисту приходится делать подобные вещи создавая временные переменные.
И это реально ненужное неудобство.
Но https://internals.rust-lang.org/t/accepting-nested-method-ca...
Вкратце суть: разрабы не хотят подпирать этот кейс ad hoc костылём, и пытаются решить более широкий класс проблем (который в свою очередь подкласс проблемы добавления интеллекта borrow-checker'у), но такие вещи надо очень аккуратно выпиливать лобзиком.
Кстати, я отмечу, это имеет и свои недостатки: чем более сложное поведение будет демонстрировать borrow-checker, тем сложнее ньюфагу будет понять как он работает. То есть, с одной стороны ньюфаг будет реже спотыкаться об острые углы borrow-checker'а, но когда он будет входить с ним в конфликт, то происходить это будет исключительно на более сложных волосатых кейсах, где понять в чём собственно проблема будет существенно сложнее.
> Не хуже всё это дело работает и в D, где
> строки иммутабельные.
Языки со сборкой мусора вообще неразумно относятся к выделениям памяти. Я даже иногда подозреваю, что программы, полагающиеся на GC, становятся тормозными не из-за того, что GC тормозной, а из-за того, что программисты при написании программ устраивают треш, угар и содомию ненужных выделений памяти.
> Ну и да, вот в таких случаях и становится понятно, зачем именно нужна перегрузка операторов.
Запиливать в язык перегрузку операторов ради нескольких случаев осмысленного применения, типа векторов да строк -- это глупость. Да, я знаю, любовно и прельстиво работать с векторами/матрицами, когда есть перегрузка операторов. Но раст идёт на компромисс и позволяет это. Он позволяет перегрузку операторов, которая не требует перегрузки функций. Я не знаю ни одного примера кода, который бы становился сильно более сложным из-за отказа использовать перегрузку операторов в пользу подхода rust'а. Но при этом несложно придумать примеры кода, где перегруженность кода перегрузкой операторов или неявными преобразованиями типов потребует для написания грамотного и безбажного кода конкретного вникания в те API которые он использует. Опеннет очень любит развлекаться над php/js, где неявное преобразование строки к целому постоянно порождает баги. И избежать таких багов можно только одним путём -- запретив слишком вольно обращаться с типами. Потому что идея "набрать высокограмотных и высококвалифицированных программистов" не работает. Она может работать некоторое время, но первый же дедлайн, когда программисту придётся писать код в стрессовых условиях и быстро, привнесёт в код десяток багов основанных на неявных преобразованиях типов или на том, что не та версия перегруженной функции была вызвана.
Кроме того, ты в курсе, что сегодня SICP не в моде и вообще выпал из трендов? Если нет, то почитай объяснения Sussman'а (одного из авторов SICP), почему это так, что изменилось в технологиях, что записало SICP в список устаревших текстов, и почему Сассман прекратил читать курс SICP в MIT по собственной инициативе 20 (прописью: ДВАДЦАТЬ) лет назад: http://www.posteriorscience.net/?p=206
Сегодня программист использует API путём метода научного^W инженерного тыка. Потому что сегодня очень много этих API и знать в деталях все невозможно. Но чтобы это работало, надо чтобы API не содержал бы всяких неявностей, которые можно найти и запомнить лишь внимательно и многократно скуривая документацию. Либо с матюгами отлаживать программу, которая опять не работает. Надо чтобы изменения API не приводили бы к тому, что код начинает работать неправильно -- должно быть иначе: либо он продолжает работать так как работал, либо он перестаёт компилироваться. Неявные преобразования типов и перегрузка функций делает это не то чтобы невозможным, но потребует от разработчиков API постоянно рассматривать все возможные use case'ы, которые они нечаянно могут сломать, если добавят в API ещё один тип или ещё одну перегруженную функцию.
Rust решает эти проблемы самым надёжным путём: он их не создаёт. Одна из причин, почему я верю в то, что rust займёт со временем достойное место среди языков программирования -- это как раз то, что он на совершенно новом уровне решает проблемы, описанные Сассманом. Я уже кидал где-то здесь ссылку на: https://onesignal.com/blog/rust-at-onesignal/ , где автор расписывает как это работает при практическом применении раста для реальных задач:
> Being able to encode constraints of your application in the type system makes it possible to
> refactor, modify, or replace large swaths of code with confidence. The type system is our
> ultimate "move quickly and don't break things" secret weapon.
Всё было бы далеко не так радужно, если бы была возможной перегрузка функций или неявные преобразования типов.
__________
Оу, кстати. В качестве оффтопа. Я как-то разглядывал sparse -- это библиотечка за авторством Торвальдса созданная для парсинга C. Она была задумана для каких-то там случаев работы с ядерным кодом, но она достаточно продвинута для того, чтобы позволить Торвальдсу в качестве демонстрации использования этой библиотечки написать компилятор C в asm. Я не знаю, используется ли эта библиотечка сейчас, но не в этом суть. Суть в том, что та sparse использует подход очень схожий с rust'овым. Только в C имплементации. Там везде используется что-то типа struct substring, которая фактически прямой аналог растового слайса -- это указатель куда-то в недра другой строки с указанием длины подстроки. И на этом подходе Торвальдс получил очень чистый, простой и понятный код, который легко перекидывает слайсы в функции, создаёт на их базе новые слайсы, возвращает их из функций, и все передачи происходят по значению, и нет необходимости дёргать malloc на каждый пунктуационный токен во входном потоке. Почитай сорцы sparse на досуге -- их чтение реально доставляет удовольствие, -- это позволит тебе понять плюсы подхода раста к работе со строками не вникая в сам раст. Только раст, в отличие от C, занимается отслеживанием lifetime'ов и избавляет от риска столкнуться с инстансом substring/slice, содержащим dangling pointer.
Кстати вот ссылка: https://sparse.wiki.kernel.org/index.php/Main_Page