Невозможно будет отличить случай сопоставления от случая присваивания. Тогда придется выбрать иной синтаксис для сопоставления, скажем, такой:
a @= 25;
вместо
let a = 25;
Вариант c let выглядит чище и традиционнее. И он как бы намекает, что перед нами все-таки не присваивание (раз он присутствует). Но многие незнакомые с языком думают, что тут именно присваивание, и что поэтому let лишний.
чтобы не угадывать по флоу какое выражение было вычислено последним
Можете уточнить, в каких ситуациях это составляет проблему? Просто посмотреть на последнюю строчку в теле функции.
Также выходит что в одних местах оправдывается многословие как необходимое, а в других оправдывается срезание синтаксиса «для выразительности»
return оправдывается как многословный потому, что он может встретиться в любой строчке внутри функции, вам его придется искать и вы должны хорошо его видеть, ибо он меняет поток выполнения программы. А вот в последней строчке он не нужен, так как явно или неявно присутствует там всегда (в Rust нет процедур) и поток выполнения там он не меняет.
Что же касается просто блоков, то там вообще оказывается не применим return, потому что это именно оператор возврата из функции, а не из текущего блока. Поэтому в блоках можно возвращать только последнее выражение.
Если данный код скомпилировался (или прошел check), то вы точно знаете, что bar возвращает (), так как другая ветка условия возвращает именно это значение благодаря ;, а типы значений веток обязательно должны совпадать.
Этот пример просто для иллюстрации данного факта, так лучше не писать. Хотя в реальном продакшн-коде я часто замечаю пропущенную точку с запятой в похожих ситуациях, когда поставить забыли или не захотели, но еще ни разу это не привело ни к какой проблеме понимания кода или к какой-то иной ошибке.
message; — просто частный пример для иллюстрации того, что происходит. В реальном коде будет foo(); с дропом в этом месте значения, которое вернула foo, который может приводить к побочным эффектом, в случае кастомизации Drop'а этого значения.
А где говорилось, что должна быть какая-то информация о структуре типа?
Вот здесь говорилось, что этой информации нет во время выполнения: "Программа, написанная на Rust, не имеет информации о типах во время выполнения, так как при компиляции происходит "стирание типов". ". Мне показалось, что вы спорили с этим утверждением.
Java-код компилируется в инструкции виртуальной машины и речь именно об этой компиляции. Во что транслируются уже эти инструкции и транслируются ли вообще, в контексте данного разговора не важно.
Вызов через точку можно рассматривать как вызов, при котором неявно передается указатель на предыдущий сегмент, а значение этого указателя определено в рантайме. Тогда как в вызове через :: сегменты являются именами статических пространств имен и не имеют представления во время выполнения. Поэтому структуры в Rust не могут иметь статических полей, в отличии от классов Java, и статические методы в Rust — это просто функции, расположенные в пространстве имен структуры, ничего более.
Не знаю как в C#, но в Java с try-with-resources ответственность за корректное освобождение ресурсов перекладывается на вызывающую сторону. К тому же не всегда использование ресурсов настолько локализовано, что безошибочное использование try-with-resources очевидно.
Использование Cleaner улучшает ситуацию и избавляет от необходимости следить за освобождением в тривиальных случаях, но в более сложных — головной боли не избежать. О проблемах освобождения ресурсов в Java подробно рассказывается в докладе Евгения Козлова "Вы все еще используете finalize()? Тогда мы идем к вам".
Any не дает никакой информации о структуре типа, он позволяет лишь получить уникальное число-идентификатор типа. И то только в том случае, если данный трейт используется. Так-то при желании вы можете руками реализовать хранение информации о типе в рантайме, но ведь речь не о том, что можно сделать руками, а о том, как язык работает по умолчанию.
В Java насколько я знаю статические методы резолвятся в рантайме, также как и не статические. Статик там означает, что вызов относится к классу, а не то, что вызов будет определен на этапе компиляции, как в Rust.
Все-таки Rust — это императивный язык, поэтому сравнение с Haskell в этом отношении немного неуместно. Например, в Haskell нет традиционного для императивных языков условия if без else, а в Rust оно есть. Ну и там, где возникает в Haskell необходимость писать императивно, появляются и фигурные скобки, и точка с запятой. Хоть опционально, но ведь почему-то они в языке присутствуют, не так ли?
Доступ к элементу массива по индексу и к значению в хэш-таблице по ключу осуществляется во время выполнения и вообще говоря для этого используется один и тот же оператор (собственные реализации типажа Index). Именно Index управляет возможностью делать доступ [i] в рантайме, это просто сахар для вызова container.index(i), однако в случае обобщенной функции требуется совершенно другая семантика — выбор функции осуществляется на этапе компиляции.
Уничтожит, но вы получите ошибку компиляции, если типы возврата не совпадут:
Да! Но можно было показать проще:
Можно и не делать. Но
;помимо этого превращает выражение в инструкцию, то есть выступает разделителем инструкций.Нет, их нужно инстанциировать, если необходимо получить объект данного типа. Как и другие структурные типы.
А вы можете сформулировать более четкие претензии к синтаксису? Я понимаю ваши чувства, но не зная их подоплеку сложно спорить конструктивно.
Отредактировал свое сообщение.
Затем, что они становятся бесполезными, если не иметь возможности их инстанциировать.
Где должны храниться значения статических полей в Rust, если после компиляции информация о типе нигде не хранится?
Невозможно будет отличить случай сопоставления от случая присваивания. Тогда придется выбрать иной синтаксис для сопоставления, скажем, такой:
вместо
Вариант c
letвыглядит чище и традиционнее. И он как бы намекает, что перед нами все-таки не присваивание (раз он присутствует). Но многие незнакомые с языком думают, что тут именно присваивание, и что поэтомуletлишний.Можете уточнить, в каких ситуациях это составляет проблему? Просто посмотреть на последнюю строчку в теле функции.
returnоправдывается как многословный потому, что он может встретиться в любой строчке внутри функции, вам его придется искать и вы должны хорошо его видеть, ибо он меняет поток выполнения программы. А вот в последней строчке он не нужен, так как явно или неявно присутствует там всегда (в Rust нет процедур) и поток выполнения там он не меняет.Что же касается просто блоков, то там вообще оказывается не применим
return, потому что это именно оператор возврата из функции, а не из текущего блока. Поэтому в блоках можно возвращать только последнее выражение.Если данный код скомпилировался (или прошел
check), то вы точно знаете, чтоbarвозвращает(), так как другая ветка условия возвращает именно это значение благодаря;, а типы значений веток обязательно должны совпадать.Этот пример просто для иллюстрации данного факта, так лучше не писать. Хотя в реальном продакшн-коде я часто замечаю пропущенную точку с запятой в похожих ситуациях, когда поставить забыли или не захотели, но еще ни разу это не привело ни к какой проблеме понимания кода или к какой-то иной ошибке.
message;— просто частный пример для иллюстрации того, что происходит. В реальном коде будетfoo();с дропом в этом месте значения, которое вернулаfoo, который может приводить к побочным эффектом, в случае кастомизацииDrop'а этого значения.Вот здесь говорилось, что этой информации нет во время выполнения: "Программа, написанная на Rust, не имеет информации о типах во время выполнения, так как при компиляции происходит "стирание типов". ". Мне показалось, что вы спорили с этим утверждением.
Java-код компилируется в инструкции виртуальной машины и речь именно об этой компиляции. Во что транслируются уже эти инструкции и транслируются ли вообще, в контексте данного разговора не важно.
Вызов через точку можно рассматривать как вызов, при котором неявно передается указатель на предыдущий сегмент, а значение этого указателя определено в рантайме. Тогда как в вызове через
::сегменты являются именами статических пространств имен и не имеют представления во время выполнения. Поэтому структуры в Rust не могут иметь статических полей, в отличии от классов Java, и статические методы в Rust — это просто функции, расположенные в пространстве имен структуры, ничего более.Ну это все же относится к оптимизации частных случаев использования и не работает в общем случае.
Какую проблему вы видите в этом коде?
Не знаю как в C#, но в Java с
try-with-resourcesответственность за корректное освобождение ресурсов перекладывается на вызывающую сторону. К тому же не всегда использование ресурсов настолько локализовано, что безошибочное использованиеtry-with-resourcesочевидно.Использование
Cleanerулучшает ситуацию и избавляет от необходимости следить за освобождением в тривиальных случаях, но в более сложных — головной боли не избежать. О проблемах освобождения ресурсов в Java подробно рассказывается в докладе Евгения Козлова "Вы все еще используете finalize()? Тогда мы идем к вам".Anyне дает никакой информации о структуре типа, он позволяет лишь получить уникальное число-идентификатор типа. И то только в том случае, если данный трейт используется. Так-то при желании вы можете руками реализовать хранение информации о типе в рантайме, но ведь речь не о том, что можно сделать руками, а о том, как язык работает по умолчанию.В Java насколько я знаю статические методы резолвятся в рантайме, также как и не статические. Статик там означает, что вызов относится к классу, а не то, что вызов будет определен на этапе компиляции, как в Rust.
Все-таки Rust — это императивный язык, поэтому сравнение с Haskell в этом отношении немного неуместно. Например, в Haskell нет традиционного для императивных языков условия
ifбезelse, а в Rust оно есть. Ну и там, где возникает в Haskell необходимость писать императивно, появляются и фигурные скобки, и точка с запятой. Хоть опционально, но ведь почему-то они в языке присутствуют, не так ли?Доступ к элементу массива по индексу и к значению в хэш-таблице по ключу осуществляется во время выполнения и вообще говоря для этого используется один и тот же оператор (собственные реализации типажа
Index). ИменноIndexуправляет возможностью делать доступ[i]в рантайме, это просто сахар для вызоваcontainer.index(i), однако в случае обобщенной функции требуется совершенно другая семантика — выбор функции осуществляется на этапе компиляции.Синтаксис с вертикальной чертой в качестве "или" довольно распространен в функциональных языках, откуда паттерн-матчинг и завезли в Rust.