Comments
благодарю!
я даже почти понял, что именно происходит в последнем листинге, пока вчитывался и переводил)
Не зря я решил раст не колупать когда то. Подобный код неподдерживаем совершенно, а значит ни один большой проект его использовать не будет. Только мелочь системную на нем наверное и можно писать. Представить себе миллион строк такого… брррр. Отсюда — не будет спроса и рабочих мест, а значит и колупать его смысла нет.
смею заметить, что писать неподдерживаемый код на других известных мне и куда более используемых языках, намного проще :)
Это вы прочитав эту статью внезапно поняли? :) Я как раз утвердился в своем ранее сформированном мнении. Но эзотерические споры я вести не стану :) Смотрим топ языков, смотрим статистику роста популярности, приходим к выводам и успокаиваемся. Первые в топах это С и Java, и совершенно понятно почему. Один прост как палка и опасен, потому для мелких проектов, второй многословен и безопасен, потому для больших проектов. Это логично и понятно потому отражено в топах. Я периодически новости с подобными рейтингами смотрю и что то своей неправоты не вижу, уже сколько лет оцениваю «что там надо изучать что бы работа была в следующем году». У меня интерес чисто практический, а не эзотерический.

Что до эзотерики, ну круто чо… как то так.
Каждый инструмент должен максимально выражать реальную одну потребность. С/Java это делают. Все где «намешано и универсально» не будет популярно никогда. Причина проста. Люди. Один человек может в совершенстве быть работником по созданию одного типа проектов. Либо «мастером ломастером на все руки». Раз люди не могут иметь десять специализаций и быть универсалами, значит и языки никогда не будут универсальными и при этом популярными.
Все где «намешано и универсально» не будет популярно никогда.

Где там у нас С++ в рейтингах?..
С# развивается довольно активно и в новые версии не стесняются добавлять разнообразные фичи.
Скала тоже довольно популярна — не как С/Java, разумеется, но работу найти вполне реально, что вполне себе показатель. А там чего только нет.


Мир не ограничивается джавой и С.

То есть, если в обозримом будущем Rust появится в «топах», вы будете учить этот язык и писать на нем «миллионы строк совершенно не поддерживаемого кода»?
Ваш вопрос аналогичен вопросу «то есть если вы внезапно окажетесь голым на Луне...». Не окажется, я достаточно ясно выразил то почему так думаю.
Первые в топах это С и Java, и совершенно понятно почему

Конечно, понятно. Появились в нужное время, имеют серьёзную поддержку (можно сравнить Оракл и Мозиллу раз уж мы в контексте раста), ну а дальше по наклонной — готовые специалисты, наличие учебных материалов, легаси и т.д. Плюс продолжают развиваться: если бы, скажем, джава остановилась несколько версий назад, то её, наверняка, потеснил C# и другие языки на JVM.


Новым языкам, ясное дело, сложнее. Это логично и я не жалуюсь на "несправедливость", если что. (:

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

Наверное, как раз поэтому ядра операционных систем, оконные менеджеры и прочие виртуальные машины Java пишут на … ой.
Мало кто будет писать целые проекты в таком стиле. На С++ можно тоже фигачить рекурсивными вариадик-темплейтами и запудрить мозги любому индусу, но обычно такие сложные места отдельно локализуются, отдельно тестируются, а остальная часть логики приложения написана более простым языком.
Посмотрите исходники boost, там тоже написано так, что без поллитра не разберёшь, и большинство разработчиков на С++ туда не полезут, но вот использовать интерфейсы библиотеки удобно.
С++ это рождение новой парадигмы. Java его обогнала именно потому, что выражает «направление полезности инструмента» лучше чем С++. Гонка языков (и я думаю, что большая часть программистов не понимает эту мысль) ведется не за «фичи и безопасность». Она ведется за «идеальное исполнение роли». Этих ролей всего три. Первая «опасный но быстрый и лаконичный» (С), вторая «безопасный, но медленный и многословный» (Java), третья «математичный» (как функциональные языки, не знаю кто тут идеал, может Haskell или даже Lisp). Кроме этих трех ролей в мире языков программирования насколько я знаю нет больше «рафинированных задач». Таким образом Rust не попадает идеально ни в одну из задач, а попадать во все не может никто, потому как люди специализированы, как я уже писал. Так что раст хорошая затея (эксперимент всегда отлично), но он всегда будет маргинальным языком.
И кстати. Всегда есть возможность, что в качестве идеального исполнителя роли найдется кто то другой, но для этого он роли должен соответствовать. Например Java появилась после С++ и тем не менее она его потеснила несмотря на «молодость» по отношению к С++ в то время и легаси код прочее подобное. Просто она лучше выражает одну из трех ролей, это единственная причина по которой тот или иной язык становятся популярны, остальное следствия.
Например Java появилась после С++ и тем не менее она его потеснила несмотря на «молодость» по отношению к С++ в то время и легаси код прочее подобное.

Ага, потеснила из тех ниш, где С++ был не лучшим выбором. Тем не менее, С++ остаётся весьма популярным и "дальнейшего вытеснения" не происходит.


Но вообще я не верю, что ты это всё серьёзно пишешь. (:

А дальнейшего вытеснения и не должно происходить. Всегда есть легаси код и прочее подобное. Плюс ко всему между «ролями» тоже жизнь есть. Я ведь не говорю «остальные не нужны», это было бы глупо. Я всего лишь говорю о том, что лучшие выразители ролей дают больше рабочих мест и их имеет смысл изучать, а те, кто между ролями дают меньше рабочих мест. Между ролями там пространства вагон и там то же есть немного работы и специализации. Это непрерывная поверхность с несколькими пиками… если включить воображение :) Но стратегия «определить, кто лучше играет роль» всегда срабатывает.

Мало того, например С++ очень близок с Java по «идеальности роли», потому они там рядом у пика тусуются. Это простая и довольно очевидная мысль по моему.
Мало того, например С++ очень близок с Java по «идеальности роли»

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


Так-то я тоже думаю, что "роли" есть, но не эти и не три.

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

Что до С++ есть один важный фактор. Java «многословна», ее поэтому именно легче поддерживать. Даже то, что она медленнее не явилось сильным недостатком. Хотя вроде бы С++ «лучше играет роль». Но роль надо описывать до конца. Безопасность, и поддерживаемость (многословность) суть роли. Потому, она опередила С++ с его магией шаблонов, перегрузкой операторов и прочим подобным. Смотришь на код через год и думаешь «еееееее». А код твой :) При этом так как суть роли «поддерживаемость», а часть поддерживаемости это безопасность и многословность (понятность кода), то С++ проигрывает хоть и не шибко уж совсем.

В этом смысле С++ ни туда, ни сюда и потому, java выигрывает по статистике и С выигрывает по статистике. По этой теории все сходится :)
извините, но «поддерживаемость» и «многословность» — это мухи и котлеты, одно с другим не пересекается никак. если уже говорить субъективно, то поддерживаемость надо разделять на «поддерживаемость языка» и «поддерживаемость конкретного кода».
в первом случае компилятор или интерпретатор нам просто не разрешит городить херню, насколько это семантически возможно. обычно для этого существуют костыли вроде линтов, тулзов статанализа, сейчас модно (и имхо очень правильно) пихать их непосредственно в компилятор. да, уровень вхождения повышается, но так со всеми областями, где нужна дисциплина, иначе это выглядит как куча под дверью — «наделал и сбежал».
второй случай — опыт в чистом его проявлении. понимаете, почти нереально «взять и написать» хорошо поддерживаемый код, не поставив себя на место поддерживающего этот код, максимально непредвзято. Кармака с его Думом не зря ставят как пример продуманности, читаемости и дисциплины, а ведь там Си, что ни кнопка — то дуло в сторону ноги. тут надо одновременно знать, что ты хочешь, и как это сделать, чтобы к тебе посреди ночи не постучались с бензопилой. это долго, тяжело и дорого, потому правила написания кода ужесточаются «сверху», авторами ЯП.
Вопрос к тем, кто знает раст.
В чем разница между ($($x:expr),*) и ($($x:expr,)*)?
Если трейт определен в одной библиотеке, тип во второй, то в третьей нельзя написать реализацию трейта этим типом?
Можно ли написать в таком случае обертку, которая автоматически реализует все трейты оборачиваемого типа?
Можно ли создать обобщение List<T: Animal, Feline> и положить туда Cat и Tiger? Т.е. получить список объектов, которые являются животными и кошачьими, но не имеют общего типа?

  1. В первом случае запятая после последнего элемента запрещена, во втором — обязательна. Вместе эти варианты покрывают оба случая, делая запятую в конце опциональной.


  2. Верно, нельзя. Чтобы реализовать трейт, нужно чтобы либо трейт, либо тип был в данном крейте.


  3. Можно сделать такую обёртку (паттерн называется newtype: struct NewType(OldType);). Нет, автоматически ничего само не реализуется, надо самому реализовать для нового типа нужные трейты. Можно облегчить себе участь, если реализовать трейт Deref, тогда трейты, методы которых принимают старый тип по ссылке (&self, &mut self), реализуются автоматом через Deref coercion.


  4. Да, можно. Через динамическую диспетчеризацию. Тип контейнера будет List<&(Animal + Filing)>. Тип элемента будет называться trait-object. Доступ к методами будет идти через таблицу виртуальных методов, на это есть строгие ограничения для трейтов (они должны быть object-safe). Полнее отвечать очень долго, лучше почитать доки, искать по западным ключевым словам :)

Отвечал с телефона, автодополнение не всегда меня понимает правильно. Так что извините за странные опечатки. Надеюсь, из контекста понятно, что имелось в виду :)

Если трейт определен в одной библиотеке, тип во второй, то в третьей нельзя написать реализацию трейта этим типом?
Насколько я знаю, обертку можно — прямую реализацию нельзя. В недавнем треде обсуждался похожий момент.

…животными и кошачьими, но не имеют общего типа?
Если оба типа реализуют один трейт, то можно обобщенно хранить их трейт объекты. Собственно, ссылка на трейт объект это жирный указатель, который хранит указатель на инстанцию и на его vtable. Поэтому можно обеспечить полиморфизм.
вот там в статье выше это упомянуто, просто автор на этом почему-то не акцентирует внимание, хотя это жуть полезная фича. если есть трейт Pet и его реализация Cat, но вы ни одной из них не владеете в текущем крейте, то вот такое сделать нельзя:
impl Pet for Cat

а вот такое уже можно:
impl Pet for Box<Cat>

потому что если у вас тип — дженерик (здесь это Box), то контролируются не его уникальность в крейте, а его конечной реализации, то есть Box<с_чем_то>. и вам не возбраняется наплодить такого:
impl Pet for Box<Cat>
impl Pet for Rc<Cat>
impl Pet for RefCell<Cat>
//  ...
impl Pet for Arc<RwLock<RefCell<Box<Cat>>>>

при этом &self (аргумент функций трейта) будет вот этого сложного типа,
Если я правильно понимаю, то тут важно то что специализация будет локальной.

Поэтому это фактически эквивалентно написанию обертки. При желании можно даже сделать некий Id<T>, который бы хранил объект и определял бы deref на него.
ЕМНИП обертки чаще пишут для дополнительной инкапсуляции, а локальная специализация обертки это просто милый побочный эффект.
Вот кстати демонстрация теории из статьи — узнать, что умеет структура (какие трейты реализует) и в случае необходимости отдать себя как соответствующий трейт, который можно раздать друзьям и поглумиться

Хороший перевод, спасибо! Разве что от "трейтов" коробит немного — почему не "типажи"? :)


Пара комментариев по сути:


В Rust это исправляемо двумя способами: расширения синтаксиса (известные как процедурные макросы) и генерация кода (build.rs) (в нестабильной ветке языка еще есть плагины к компилятору — прим. пер.).

Процедурные макросы — это подвид плагинов компилятора. https://doc.rust-lang.org/book/compiler-plugins.html#syntax-extensions


In addition to procedural macros, you can define new derive-like attributes and other kinds of extensions.

И всё это работает только в nightly.




// То же, с обобщенным типом!
impl Equal<i32> for u32 {
    fn equal(&self, other: &i32) -> Self {
        if *other < 0 {
            false
        } else {
            *self == *other as u32
        }
    }
}

Не понял, где тут обобщённый тип. Предыдущий пример


impl Equal<u32> for u32 {
    fn equal(&self, other: &u32) -> Self {
        *self == *other
    }
}

делал то же самое, просто для u32.

Разве что от "трейтов" коробит немного — почему не "типажи"? :)

В курсе, что это уже устоявшийся термин в русскоязычном сообществе, но лично меня коробят как раз "типажи". (:

Хотя спустя два года руки чешутся все переписать на «типажи»…
там чуть разные условия, автор скорее всего пытался акцентировать внимание на то, что во втором случае у нас обобщенный трейт не для родного типа u32, а для i32, и кастится в конце.
impl Equal<u32> for u32 {
    fn equal(&self, other: &u32) -> Self {
        *self == *other
    }
}

// Taking advantage of that sweet generic trait!
impl Equal<i32> for u32 {
    fn equal(&self, other: &i32) -> Self {
        if *other < 0 {
            false
        } else {
            *self == *other as u32
        }
    }
}


трейт имхо термин более понятный — человек, привыкающий к Rust видит его в первую очередь как trait. типаж же термин более широко применимый, но в более узких кругах функциональных и матпрограммистов.
Only those users with full accounts are able to leave comments. Log in, please.