А. Вы уверены что средний человек способен конструктивно проводить переизбыток свободного времени? Потому что если нет, то он просто будет сидеть на антидепрессантах или каком-нибудь другом наркотике, весь день смотреть ТВ/ютуб и в целом быть несчастным.
Б. Для человека, как для любого другого животного, труд это нормально. Мы эволюцировали для того чтобы преодолевать трудности по жизни. Если у человека это отнять, то он начнёт потихоньку сходить с ума (возвращаемся к пункту А).
В. Аристократы не просто отдыхали пожизненно, они вообще-то служили монарху, в том числе они были обязаны служить в армии или на чиновьичей должности. Так что у них были и обязанности, не только привилегии.
Г. Почему вы считаете весь ручной труд чем-то унизительным? Не поверите, есть куча слесарей, электриков и так далее, которые гордятся своей работой, которым приятно делать что-то руками (вместо того чтобы просиживать штаны в офисе). Да, давайте отберём у дяди Васи смысл его жизни и скажем "вот, теперь вы свободный человек!".
В голанге достаточно своих способов стрелять в ноги, не говоря об очень ограниченной выразительности языка.
Если говорить о примере из статьи:
С int и float там всё в порядке, типизация строгая;
С Quantity и Price в принципе нормально. Разве что там где раст потребует явно указывать тип даже у статических значений (например, можно указать только Quantity(100)), го допускает простое 100 (скорее минус по читаемости, чем по наличию багов).
На счёт парсинга строки. Проверка ошибок в го не обязательна, т.к. результат и ошибка возвращаются как два отдельных значения (а не одним значением как в раст). Программист вполне может проигнорировать (или забыть) проверку err значения и использований невалидный результат. К счастью ошибочный результат парсинга будет ноль, но в общем случае никто не гарантирует что там безопасное значение. Конечно есть библиотеки для го, которые добавляют генерик Either тип, но из-за того что в го нет нормальных енамов и генериков, использовать Either тип это тот ещё геморрой.
Отдельное веселье это парсинг строк в го, потому что динозавровая стандартная библиотека ещё до генериков не доросла. Там ParseUint всегда возвращает uint64. Если вам нужен какой-нибудь uint16, то придётся делать конвертацию uint64 -> uint16, за что на вас уже накричат линтеры (в общем случае то преобразование небезопасное)...
Если на String вызвать Into<String>, то по-сути никакой работы сделано не будет: внутри вызовется From<String> на String, который вернёт эту же самую строку, ничего с ней не сделав. Если на String вызвать ToString, то там внутри будет клонирование, т.е. будет выделение памяти на куче (потому что ToString принимает &self, в отличии от Into<String>). А вот вызовы ToString и Into<String> на &str будут идентичными. То есть Into<String> позволит передавать владение String, там где есть такая возможность.
P.S. Если вы ожидаете, что в функцию можно передавать только &str или String, то лучше вместо ToString использовать Into<String>. Во-первых, Into<String> будет эффективнее. Во-вторых, с ToString можно напороться на то что в функцию можно передать тот же i32.
Что-то мне хочется не согласиться. В каких случаях это причинит боль? Мне кажется любая функция, которой не нужно владеть строкой, вполне может принимать &str.
ToString это хороший подход, но он применим в основном в апи, чтобы уюзеру было удобно, а внутри кода крейта обычно что-то одно передаёшь, или &str, или String.
Универсальный переносимый сервер на двух потоках можно и на Rust написать. Это мне кажется больше обучающая статья про async, поэтому некоторые примеры могут быть ходульные (что в целом распространённая проблема обучающих статей). Главное что показывает как async работает.
А в чём собственно проблема писать "э" там где на слух "э"? Особенно учитывая что это слова, которые в языке недавно и с их написанием можно экспериментировать.
Если говорить о расте, часть ооп характеристик у него есть - инкапсуляция и полиморфизм. Только наследования нет, что на самом деле в крупном софте может быть плюсом (отсутствие наследования делает код проще для понимания имхо).
А так, даже без вообще каких-либо фишек ооп крупный софт пишут - ядро Линукс например
Ну извините. В вашем мире походу вообще должна быть только одна операционка, одна архитектура с одним набором инструкци...
Это вы о чём?
Так же в вашем идеальном мире...
Откуда вы про идеальный мир взяли? Пакетные менеджеры и средства сборки это что-то что есть в применяющихся на практике ЯП.
Я не говорю, что это хорошо, что в плюсах это не совсем тривиально
Вы по крайней мере не считаете это проблемой, а если кто-то считает, то "пусть идёт программировать на языках для ненастоящих программистов". (Я ни в коем случае не адвокат js и питона, просто вы тут ложную дихотомию устроили).
Но это достаточно гибко и всё зависит от потребностей
Да и в языках с менеджерами зависимостей всё гибко. Только там не надо ломать голову над типовым сценарием, всё работает из коробки.
Потому что программист должен изучать арбитрарный набор всякого (часто) плохо документированного инструментария с кривым интерфейсом и недостатком примеров, чтобы сделать совершенно типовую рутинную задачу (собрать проект или добавить в него зависимость). Потом он прийдёт в другой проект, а там свои погремушки, поэтому надо учить новый набор опенсорсных вкусностей. (Не важно что все эти QOL вещи есть в любом современном языке, не зависимо от его низко/высоко-уровневости, если ты не страдаешь по пустякам, то ты не Труъ программист)
Говнокод само собой можно везде написать, но всегда нужно ещё учитывать человеческий фактор. Некоторые ошибки проще совершить в одном языке и сложнее в другом. Просто программисты на c++ любят писать что ошибки с памятью делают только нубы, а "тру" программисты ошибок не делают и память освобождают. Но ведь это сложно всё время быть начеку, особенно в больших проектах, хоть раз да потеряешь бдительность ошибёшься. И раст пытается устранить некоторые ошибки, которые возникают из-за человеческого фактора, уменьшает ментальную нагрузку на программиста, там где это можно сделать. (Не являюсь пропагандистом раста в плане "нужно всё на нём переписать", хоть и люблю его, но всё таки некоторые преимущества по сравнению с плюсами у него есть)
Вопрос, что вы пытаетесь сказать? Что этот паттерн неприменим в случаях когда скопировать 32 байта это дорого? Наверное, но это достаточно редкий случай, мне кажется в подавляющем большинстве производительность при наивной реализации будет более чем достаточной, в остальных случаях уже надо смотреть на конкретную задачу и сиходя из неё придумывать оптимизацию.
Option держит ссылку если ему явно сказать: Option<&int> или если храните более долговременные данные, то с умным указателем Option<Rc<int>>. ИМХО в случае с User<Editor> это скорее всего не затратно, потому что компилятор догадается, что сама структура не меняется (меняется PhantomData только, которая в памяти не хранится, а есть только на этам компиляции) и не будет копировать.
P.S. Такой перенос данных от старого билдера к новому (копирование) это в худшем случае мемкопи, в лучшем компилятор поймёт что мы делаем и не будет билдер менять никак. То есть никакого дополнительного выделения динамической памяти производится не будет, если у нас внутри билдера есть поля с динамической памятью.
PhantomData нужна по единственной причине: rust запрещает добавлять в структуру генерик параметр и при этом не использовать его внутри самой структуры. Поэтому в неё добавляется поле PhantomData<State>. Больше PhantomData ничего не делает.
А. Вы уверены что средний человек способен конструктивно проводить переизбыток свободного времени? Потому что если нет, то он просто будет сидеть на антидепрессантах или каком-нибудь другом наркотике, весь день смотреть ТВ/ютуб и в целом быть несчастным.
Б. Для человека, как для любого другого животного, труд это нормально. Мы эволюцировали для того чтобы преодолевать трудности по жизни. Если у человека это отнять, то он начнёт потихоньку сходить с ума (возвращаемся к пункту А).
В. Аристократы не просто отдыхали пожизненно, они вообще-то служили монарху, в том числе они были обязаны служить в армии или на чиновьичей должности. Так что у них были и обязанности, не только привилегии.
Г. Почему вы считаете весь ручной труд чем-то унизительным? Не поверите, есть куча слесарей, электриков и так далее, которые гордятся своей работой, которым приятно делать что-то руками (вместо того чтобы просиживать штаны в офисе).
Да, давайте отберём у дяди Васи смысл его жизни и скажем "вот, теперь вы свободный человек!".
И правда. Пожалуй это недостаток, что более опасный способ сделан как более лёгкий для использования (x as u64 проще написать чем u64::try_from(x))
В голанге достаточно своих способов стрелять в ноги, не говоря об очень ограниченной выразительности языка.
Если говорить о примере из статьи:
С int и float там всё в порядке, типизация строгая;
С Quantity и Price в принципе нормально. Разве что там где раст потребует явно указывать тип даже у статических значений (например, можно указать только
Quantity(100)), го допускает простое100(скорее минус по читаемости, чем по наличию багов).На счёт парсинга строки. Проверка ошибок в го не обязательна, т.к. результат и ошибка возвращаются как два отдельных значения (а не одним значением как в раст). Программист вполне может проигнорировать (или забыть) проверку err значения и использований невалидный результат. К счастью ошибочный результат парсинга будет ноль, но в общем случае никто не гарантирует что там безопасное значение.
Конечно есть библиотеки для го, которые добавляют генерик Either тип, но из-за того что в го нет нормальных енамов и генериков, использовать Either тип это тот ещё геморрой.
Отдельное веселье это парсинг строк в го, потому что динозавровая стандартная библиотека ещё до генериков не доросла. Там ParseUint всегда возвращает uint64. Если вам нужен какой-нибудь uint16, то придётся делать конвертацию uint64 -> uint16, за что на вас уже накричат линтеры (в общем случае то преобразование небезопасное)...
А как вы в Rust сделаете это некорректное преобразование в u64?
Если на String вызвать Into<String>, то по-сути никакой работы сделано не будет: внутри вызовется From<String> на String, который вернёт эту же самую строку, ничего с ней не сделав.
Если на String вызвать ToString, то там внутри будет клонирование, т.е. будет выделение памяти на куче (потому что ToString принимает &self, в отличии от Into<String>).
А вот вызовы ToString и Into<String> на &str будут идентичными.
То есть Into<String> позволит передавать владение String, там где есть такая возможность.
P.S. Если вы ожидаете, что в функцию можно передавать только &str или String, то лучше вместо ToString использовать Into<String>.
Во-первых, Into<String> будет эффективнее.
Во-вторых, с ToString можно напороться на то что в функцию можно передать тот же i32.
Что-то мне хочется не согласиться. В каких случаях это причинит боль? Мне кажется любая функция, которой не нужно владеть строкой, вполне может принимать &str.
ToString это хороший подход, но он применим в основном в апи, чтобы уюзеру было удобно, а внутри кода крейта обычно что-то одно передаёшь, или &str, или String.
Универсальный переносимый сервер на двух потоках можно и на Rust написать.
Это мне кажется больше обучающая статья про async, поэтому некоторые примеры могут быть ходульные (что в целом распространённая проблема обучающих статей). Главное что показывает как async работает.
А в чём собственно проблема писать "э" там где на слух "э"? Особенно учитывая что это слова, которые в языке недавно и с их написанием можно экспериментировать.
Если говорить о расте, часть ооп характеристик у него есть - инкапсуляция и полиморфизм. Только наследования нет, что на самом деле в крупном софте может быть плюсом (отсутствие наследования делает код проще для понимания имхо).
А так, даже без вообще каких-либо фишек ооп крупный софт пишут - ядро Линукс например
Из статьи:
Это вы о чём?
Откуда вы про идеальный мир взяли? Пакетные менеджеры и средства сборки это что-то что есть в применяющихся на практике ЯП.
Вы по крайней мере не считаете это проблемой, а если кто-то считает, то "пусть идёт программировать на языках для ненастоящих программистов".
(Я ни в коем случае не адвокат js и питона, просто вы тут ложную дихотомию устроили).
Да и в языках с менеджерами зависимостей всё гибко. Только там не надо ломать голову над типовым сценарием, всё работает из коробки.
Потому что программист должен изучать арбитрарный набор всякого (часто) плохо документированного инструментария с кривым интерфейсом и недостатком примеров, чтобы сделать совершенно типовую рутинную задачу (собрать проект или добавить в него зависимость).
Потом он прийдёт в другой проект, а там свои погремушки, поэтому надо учить новый набор опенсорсных вкусностей.
(Не важно что все эти QOL вещи есть в любом современном языке, не зависимо от его низко/высоко-уровневости, если ты не страдаешь по пустякам, то ты не Труъ программист)
Говнокод само собой можно везде написать, но всегда нужно ещё учитывать человеческий фактор. Некоторые ошибки проще совершить в одном языке и сложнее в другом. Просто программисты на c++ любят писать что ошибки с памятью делают только нубы, а "тру" программисты ошибок не делают и память освобождают. Но ведь это сложно всё время быть начеку, особенно в больших проектах, хоть раз да потеряешь бдительность ошибёшься. И раст пытается устранить некоторые ошибки, которые возникают из-за человеческого фактора, уменьшает ментальную нагрузку на программиста, там где это можно сделать.
(Не являюсь пропагандистом раста в плане "нужно всё на нём переписать", хоть и люблю его, но всё таки некоторые преимущества по сравнению с плюсами у него есть)
Передача владения это концепция на уровне языка. Под капотом в некоторых случаях будет производится копирование.
Вопрос, что вы пытаетесь сказать? Что этот паттерн неприменим в случаях когда скопировать 32 байта это дорого? Наверное, но это достаточно редкий случай, мне кажется в подавляющем большинстве производительность при наивной реализации будет более чем достаточной, в остальных случаях уже надо смотреть на конкретную задачу и сиходя из неё придумывать оптимизацию.
Option держит ссылку если ему явно сказать: Option<&int> или если храните более долговременные данные, то с умным указателем Option<Rc<int>>.
ИМХО в случае с User<Editor> это скорее всего не затратно, потому что компилятор догадается, что сама структура не меняется (меняется PhantomData только, которая в памяти не хранится, а есть только на этам компиляции) и не будет копировать.
В целом я согласен, но это уже детали реализации, суть паттерна это не меняет.
P.S. Такой перенос данных от старого билдера к новому (копирование) это в худшем случае мемкопи, в лучшем компилятор поймёт что мы делаем и не будет билдер менять никак. То есть никакого дополнительного выделения динамической памяти производится не будет, если у нас внутри билдера есть поля с динамической памятью.
Да, там данные полностью перемещаются из билдера в билдер. Что-то вроде:
Есть ли в этом проблема?
PhantomData нужна по единственной причине: rust запрещает добавлять в структуру генерик параметр и при этом не использовать его внутри самой структуры. Поэтому в неё добавляется поле PhantomData<State>. Больше PhantomData ничего не делает.