Комментарии 34
Прекрасный перевод. Спасибо!
Отличный перевод прекрасного выноса мозга :)
Что до эзотерики, ну круто чо… как то так.
Все где «намешано и универсально» не будет популярно никогда.
Где там у нас С++ в рейтингах?..
С# развивается довольно активно и в новые версии не стесняются добавлять разнообразные фичи.
Скала тоже довольно популярна — не как С/Java, разумеется, но работу найти вполне реально, что вполне себе показатель. А там чего только нет.
Мир не ограничивается джавой и С.
Первые в топах это С и Java, и совершенно понятно почему
Конечно, понятно. Появились в нужное время, имеют серьёзную поддержку (можно сравнить Оракл и Мозиллу раз уж мы в контексте раста), ну а дальше по наклонной — готовые специалисты, наличие учебных материалов, легаси и т.д. Плюс продолжают развиваться: если бы, скажем, джава остановилась несколько версий назад, то её, наверняка, потеснил C# и другие языки на JVM.
Новым языкам, ясное дело, сложнее. Это логично и я не жалуюсь на "несправедливость", если что. (:
Наверное, как раз поэтому ядра операционных систем, оконные менеджеры и прочие виртуальные машины Java пишут на … ой.
Посмотрите исходники boost, там тоже написано так, что без поллитра не разберёшь, и большинство разработчиков на С++ туда не полезут, но вот использовать интерфейсы библиотеки удобно.
Например Java появилась после С++ и тем не менее она его потеснила несмотря на «молодость» по отношению к С++ в то время и легаси код прочее подобное.
Ага, потеснила из тех ниш, где С++ был не лучшим выбором. Тем не менее, С++ остаётся весьма популярным и "дальнейшего вытеснения" не происходит.
Но вообще я не верю, что ты это всё серьёзно пишешь. (:
Мало того, например С++ очень близок с Java по «идеальности роли», потому они там рядом у пика тусуются. Это простая и довольно очевидная мысль по моему.
Мало того, например С++ очень близок с Java по «идеальности роли»
Это как? Ты говоришь о нескольких ролях, в контексте С++ нас две интересует: "опасный но быстрый и лаконичный" и "безопасный, но медленный и многословный". С++ более безопасный чем С и более быстрый чем джава. То есть, он ни туда ни сюда, если следовать этой "теории".
Так-то я тоже думаю, что "роли" есть, но не эти и не три.
Что до С++ есть один важный фактор. Java «многословна», ее поэтому именно легче поддерживать. Даже то, что она медленнее не явилось сильным недостатком. Хотя вроде бы С++ «лучше играет роль». Но роль надо описывать до конца. Безопасность, и поддерживаемость (многословность) суть роли. Потому, она опередила С++ с его магией шаблонов, перегрузкой операторов и прочим подобным. Смотришь на код через год и думаешь «еееееее». А код твой :) При этом так как суть роли «поддерживаемость», а часть поддерживаемости это безопасность и многословность (понятность кода), то С++ проигрывает хоть и не шибко уж совсем.
В этом смысле С++ ни туда, ни сюда и потому, java выигрывает по статистике и С выигрывает по статистике. По этой теории все сходится :)
в первом случае компилятор или интерпретатор нам просто не разрешит городить херню, насколько это семантически возможно. обычно для этого существуют костыли вроде линтов, тулзов статанализа, сейчас модно (и имхо очень правильно) пихать их непосредственно в компилятор. да, уровень вхождения повышается, но так со всеми областями, где нужна дисциплина, иначе это выглядит как куча под дверью — «наделал и сбежал».
второй случай — опыт в чистом его проявлении. понимаете, почти нереально «взять и написать» хорошо поддерживаемый код, не поставив себя на место поддерживающего этот код, максимально непредвзято. Кармака с его Думом не зря ставят как пример продуманности, читаемости и дисциплины, а ведь там Си, что ни кнопка — то дуло в сторону ноги. тут надо одновременно знать, что ты хочешь, и как это сделать, чтобы к тебе посреди ночи не постучались с бензопилой. это долго, тяжело и дорого, потому правила написания кода ужесточаются «сверху», авторами ЯП.
В чем разница между ($($x:expr),*) и ($($x:expr,)*)?
Если трейт определен в одной библиотеке, тип во второй, то в третьей нельзя написать реализацию трейта этим типом?
Можно ли написать в таком случае обертку, которая автоматически реализует все трейты оборачиваемого типа?
Можно ли создать обобщение List<T: Animal, Feline> и положить туда Cat и Tiger? Т.е. получить список объектов, которые являются животными и кошачьими, но не имеют общего типа?
В первом случае запятая после последнего элемента запрещена, во втором — обязательна. Вместе эти варианты покрывают оба случая, делая запятую в конце опциональной.
Верно, нельзя. Чтобы реализовать трейт, нужно чтобы либо трейт, либо тип был в данном крейте.
Можно сделать такую обёртку (паттерн называется newtype:
struct NewType(OldType);
). Нет, автоматически ничего само не реализуется, надо самому реализовать для нового типа нужные трейты. Можно облегчить себе участь, если реализовать трейт Deref, тогда трейты, методы которых принимают старый тип по ссылке (&self, &mut self), реализуются автоматом через Deref coercion.
- Да, можно. Через динамическую диспетчеризацию. Тип контейнера будет
List<&(Animal + Filing)>
. Тип элемента будет называться trait-object. Доступ к методами будет идти через таблицу виртуальных методов, на это есть строгие ограничения для трейтов (они должны быть object-safe). Полнее отвечать очень долго, лучше почитать доки, искать по западным ключевым словам :)
Если трейт определен в одной библиотеке, тип во второй, то в третьей нельзя написать реализацию трейта этим типом?Насколько я знаю, обертку можно — прямую реализацию нельзя. В недавнем треде обсуждался похожий момент.
…животными и кошачьими, но не имеют общего типа?Если оба типа реализуют один трейт, то можно обобщенно хранить их трейт объекты. Собственно, ссылка на трейт объект это жирный указатель, который хранит указатель на инстанцию и на его vtable. Поэтому можно обеспечить полиморфизм.
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.
Разве что от "трейтов" коробит немного — почему не "типажи"? :)
В курсе, что это уже устоявшийся термин в русскоязычном сообществе, но лично меня коробят как раз "типажи". (:
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. типаж же термин более широко применимый, но в более узких кругах функциональных и матпрограммистов.
Куча способов переиспользовать код в Rust