LINUX.ORG.RU

Rust: преобразования указателя на трейт в конкретный тип

 


1

7

Хочу хранить мапу с экземплярами реализующими трейт. При этом на месте одного ключа я уверен, что будет всегда лежать экземпляр одной и той же реализации (в данном примере в качестве ключа выступает помимо прочего TypeId). С хранием проблем не возникает, возникает проблема с извлечением элементов.

use std::collections::HashMap;
use std::any::TypeId;
use std::rc::Rc;

trait MyTrait {
    fn new() -> Self where Self: Sized;

    // Тут ещё какие-нибудь методы
}

struct MyStruct {
    items: HashMap<(i32, TypeId), Rc<dyn MyTrait>>
}

impl MyStruct {
    fn get<T: MyTrait + 'static>(&mut self, key: i32) -> Rc<T> {
        if let Some(item) = self.items.get(&(key, TypeId::of::<T>())) {
            item.clone() as Rc<T>
        } else {
            let item = Rc::new(T::new());
            self.items.insert((key, TypeId::of::<T>()), item.clone());
            item
        }
    }
}

Как правильно конвертировать элемент при его извлечении в тип T?

★★★★★

Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от khrundel

Смотри, вот я кладу в enum твой днищенский два типа. У обоих есть метод foo. Очевидно, что зная о типах мы знаем то, что этот foo можно вызвать без всякого match.

Вот C++ знает это - я пишу f(auto x) x.foo(), т.е. не может в полиморфизм, но может нормально в сум-типы, поэтому я пишу f<T: a|b>(x) x.foo() ts понимает, что foo есть в обоих типах.

Твой же сахарок - ничего не знает о типах. Это убогий рантайм-костыль. Поэтому ты будешь писать match там, где он не нужен.

right_security
()
Ответ на: комментарий от khrundel

Ну вот я даю тебе Shape* ptr. Сохрани его в новомодный формат xml. Shape, напоминаю, это базовый класс.

Покажи мне базовый класс в расте для начала.

Видишь как классно, всё в одном месте. Не раскидано по нескольким трейтам, и тем более на напихано внутрь Shape.cpp, Circle.cpp, Line.cpp. Если мне надо рисовать эти шейпы в разных графических библиотеках мне не придётся извращаться и пилить 100500 перегрузок draw в каждом классе.

Что ты за мусор родил? Ты мне давай, с реализация. А не эту фейчину. Хотя на уровне псевдокода.

Смысла в этом мусоре ровно ноль. Но я могу добавить в этот ноль немного смысла. В C++ это будет write(auto) - более ничего ненужно.

Но в целом покажи нормальный пример, который можно сравнивать.

Да ты шо? Чё, правда если добавить новый вариант то нужно везде где с ним работали этот вариант предусмотреть?

Куда работал? Ничего ненужно - это в базе. Само всё работает. Ненужно путать своё непотребство из 60-70 и цпп, язык 21 века.

Чувак, уже до тебя написали что визит очень хорош и им можно несколько веток одной строчкой покрыть. Никто с этим не спорит. Вот только хорошо ли это? А если семантически «то же решение» не подходит, а компилятор тебя даже не предупредил?

Не, семантически «то же решение» не может не подходит. С++ это такой язык, где семантичность - абсолют.

Ты перепутал своё непотребство и С++. Вот у тебя да, добавишь _ в матч и всё, всё сломается. Когда добавишь новый матч и не узнаешь об этом. А вот в цпп будет ошибка.

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

Ты ничего не знаешь об этих конструкциях. Ты их не понимаешь. Там где я пишу одну строчку - ты на расте пишешь тысячу.

Ты мыслишь как раст-апдет. ТЫ думаешь, что эта конструкция как и утебя - работает только в одном случае. Но нет - она работает всегда. В этом и фишка.

right_security
()
Ответ на: комментарий от khrundel

Я даже разберу твоя пример. Очевидно, что он родился просто из непонимания и ограничений раста.

enum Shape{
  Line(...)
  Circle(...),
}

Вот что это за днище? Что ты там скрыл за … - это line(p0,p1) - это чушь. Потому как тогда я не смогу пользоваться линией отдельно. Если там Line(line) - это уже похоже на что-то настоящие, но вот в чём проблема. В С++ это ненужно.

В С++ ты просто пишешь:

auto write(line) {...}
auto write(circle) {...}

На самом деле даже это ненужно в С++ - он может в полиморфизм и можно написать write сразу для всех агрегатов. Единственное что нужно мапинг для агрегатов сделать. В расте тебе никакой мапинг не может.

Если взять рефлексия-либу/либо реализацию рефлекси - мапинг будет готовый.

Для нормального протокола мапинг вообще ненужен. Мапинг нужен только для получения имён полей. Если там запись вида Line(int,int) - имён полей у тебя в принципе нет. Как их нет и в таплах любых иных.

Далее у меня будет код:

line x; write(x);
circle y; write(y);

Если мне нужно будет засунуть это в вектор - я добавлю variant. Только вот это практически никогда ненужно. А ты в расте обязан писать этот мусор даже тогда, когда писать в вектор это ненужно.

В этом проблема. Не говоря уже о том, что у тебя везде будет рантайм-диспатч. Даже там где этого ненужно.

При этом если я захочу добавить write для варианта - как это сделать я тебе показывал.

В результате у тебя будет работать как и то, что выше. Так и:

write(shape{x});
write(shape{y});
right_security
()
Ответ на: комментарий от right_security

Смотри, вот я кладу в enum твой днищенский два типа. У обоих есть метод foo. Очевидно, что зная о типах мы знаем то, что этот foo можно вызвать без всякого match.

Неочевидно. Более того, неверно. Утиная типизация до добра не доволит.

У класса Shape есть метод Draw и у класса Swordsman есть метод Draw, но они не взаимозаменяемы.

Или, если брать более реалистичный пример, у тебя может быть 2 источника данных, синхронный и асинхронный, например. Если ты добавляешь асинхронный и в нём так же есть метод Read, только который возвращает не код ошибки, на который ты привык забивать, а Promise. Вряд ли ты захочешь, чтоб твоё auto-говно молча скомпилировалось и не подсказало тебе где появляется проблема.

Вообще, поэтому плюсы и говно, их собирала сорока, тащившая в язык всё блестящее. В плюсах есть и кроссплатформенный ассемблер C, и ООП, и обобщённое программирование, и даже элементы функциональщины и отдельный декларативный язык для написания шаблонов, и вот эта самая утиная типизация с её SFINAE. Всё это сшито белыми нитками, а постаревшая хипстота рассказывает о «мэджик», получающийся если пролезть без мыла между 2мя заплатками.

khrundel ★★★★
()
Ответ на: комментарий от right_security

Покажи мне базовый класс в расте для начала.

Он не нужен. В расте есть трейты и нормальные типы-суммы.

Ты долго будешь вертеть жопой чтоб не признавать, что твой сраный полиморфизм не решит нормально проблему отсутствия sum type?

Что ты за мусор родил? Ты мне давай, с реализация.

Слив засчитан, лошара. Извини, писать тебе очевидную реализацию сохранения кружков в xml я не буду

В C++ это будет write(auto) - более ничего ненужно.

В C++ будет auto (auto(auto, auto)), я понял.

А теперь не верти жопой и пили мне код для сохранения Shape* ptr в xml. Непосредственно код для записи можешь скипнуть, мне достаточно чтоб было ясно куда вставлять «тут сохраняем круг»,

Куда работал? Ничего ненужно - это в базе. Само всё работает.

Именно поэтому и придумывали всякие liskov-принципы, чтоб это «всё работает» ногу не отстрелило.

Не, семантически «то же решение» не может не подходит. С++ это такой язык, где семантичность - абсолют.

4.2

Ты ничего не знаешь об этих конструкциях. Ты их не понимаешь. Там где я пишу одну строчку - ты на расте пишешь тысячу.

И это 4.2.

Видишь, всё что нужно было - предложить тебе примитивнейшую задачу сериализации существующей плюсовой иерархии в новый формат, и ты поплыл.

khrundel ★★★★
()
Ответ на: комментарий от right_security

Я даже разберу твоя пример. Очевидно, что он родился просто из непонимания и ограничений раста.

попробуй.

Вот что это за днище? Что ты там скрыл за … - это line(p0,p1) - это чушь.

Нет.

Потому как тогда я не смогу пользоваться линией отдельно.

Зачем тебе пользоваться этим отдельно? Нет, в некоторых случаях, конечно, такая необходимость бывает, но в большинстве случаев, как в данном примере с линией, 2 точки тебе достаточны. А для круга - точка и радиус. То, что ты привык писать огромный класс для линии - это проблема твоих плюсов, тебе там нужен конструктор, а потом раз уж взялся, напишешь пару операций, чтоб твой класс описывал объект «линия», потом придётся добавить виртуальную функцию для сохранения в xml. Раст отказывается от этой идеи. Данные есть данные, 2 точки так 2 точки, незачем городить кучу мусора.

В С++ ты просто пишешь:

auto write(line) {...}
auto write(circle) {...}

Это неправда. Хранить ты будешь свои шейпы в общем контейнере, так что никакое auto write у тебя не сработает, потребуется vtable, а значит засрёшь свои красивые классы кодом для сохранения в xml. Причём напишешь и разбросаешь по 10 файлов функции с телами по 1 строчке

Если мне нужно будет засунуть это в вектор - я добавлю variant.

А что случилось? Зачем пилить кривой аналог enuma когда есть божественный полиморфизм?

Только вот это практически никогда ненужно.

Это практически всегда нужно. Ты хоть что-то кроме хеллоуворлдов писал вообще? Более того, твой любимый полиморфизм ради того и делался. Если бы ситуация «X может быть А или Б или В» была реально редкой, весь твой полиморфизм сводился бы к подключению больших объектов по API, там в принципе можно было таблицы вызовов руками заполнять и не париться. Нет, полиморфизм добавили именно из предположения, что ситуаций «линия или круг» будет много, поэтому надо создание виртуальных таблиц автоматизировать.

Если взять рефлексия-либу/либо реализацию рефлекси - мапинг будет готовый.

Ссаными тряпками тебя надо гнать из секты фанатиков плюсов. Какая нахрен рефлексия? Это ж нарушение инкапсуляции.

Далее у меня будет код:

line x; write(x);
circle y; write(y);

Какой в этом смысл? У тебя перегрузка головного мозга. В C написали бы

line x; line_write(x);
circle y; circle_write(y);

твои божественные плюсы просто избавили от необходимости писать префикс функции. А вот простейший вариант «взять массив шейпов и сохранить в xml, не засирая при этом все классы» чистые плюсы уже не осилили, сначала попытались через динамик каст, потом плюнули и добавили вариант, но так как матча не было, а был визитор, пришлось мучиться с разграничением по типу. Получился вот такой кадавр, хранит варианты по индексу, извлекает типу.

khrundel ★★★★
()
Ответ на: комментарий от eao197

Я думал об этом, но он ни разу не назвал раст скриптухой и не сказал что что-то они украли. Так что даже не знаю.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Неочевидно. Более того, неверно. Утиная типизация до добра не доволит.

Меня не интересуют твои оправдания. Она не утиная. Это твои фантазии. К тому же, зачем ты съезжаешь? Ты мне там что рассказывал? Что тебе непонятно про уровень типов. Тебе объяснили. Ты почему-то начала оправдываться вместо признания очевидного.

У класса Shape есть метод Draw и у класса Swordsman есть метод Draw, но они не взаимозаменяемы.

С чего вдруг?

Или, если брать более реалистичный пример, у тебя может быть 2 источника данных, синхронный и асинхронный, например. Если ты добавляешь асинхронный и в нём так же есть метод Read, только который возвращает не код ошибки, на который ты привык забивать, а Promise.

О, этот уровень аргументации. Прям поражает воображение. Этот уровень понимания туда же.

Удивительно, но раст-фанатик с unwrap/expect/panic будет мне рассказывать про ошибка, хотя в расте ошибки обработать невозможно.

Вряд ли ты захочешь, чтоб твоё auto-говно молча скомпилировалось и не подсказало тебе где появляется проблема.

С чего вдруг не захочу? Если оно собирается - оно корректно. В Этом суть. Если я не использую никак результат функции, либо он подходит по все использования - никакой ошибки нет. Функция делает то, что должна.

Это, конечно, сложно понять. Как рабу понять что такое свобода, но ты попытайся.

Вообще, поэтому плюсы и говно, их собирала сорока, тащившая в язык всё блестящее.

Ты это про раст?

В плюсах есть и кроссплатформенный ассемблер C, и ООП, и обобщённое программирование, и даже элементы функциональщины и отдельный декларативный язык для написания шаблонов, и вот эта самая утиная типизация с её SFINAE. Всё это сшито белыми нитками, а постаревшая хипстота рассказывает о «мэджик», получающийся если пролезть без мыла между 2мя заплатками.

Зачем мне эти сказки рассказываешь? C++ - это и есть си. ООП в клюсах быть не может - они его создали. Как можно утащить то, что ты создал?

Обобщённое программирование - это полиморфизм. Никакой «функциональщины не существует» - это какие-то пропагандисткие штампы. Мне как-то они мимо.

Какой такой декларативный язык, да ещё отдельный? Ты откуда этой чуши набрался? Показывай мне этот язык. Вот в немощном расте я могу показать - это макросня, а ты мне в цпп покажи.

Никакой утиной типизации не существует. Это так же твои фантазии. Есть полиморфизм. Никто более в него не может, но это твои проблемы. Что такое sfinae ты даже не знаешь. Это базовая часть полиморфизма.

В том же ts оно тоже есть - особый кейс для never в преобразованиях типов. Просто ты не знаешь что такое типы. В твоих днище-сахарке их нет, вернее есть сишный огрызок и пару фокусов.

right_security
()
Ответ на: комментарий от khrundel

Он не нужен. В расте есть трейты и нормальные типы-суммы.

У тебя тип-сумм нет. Это уже было доказано мною не раз. Они есть в ts, а у тебя мусорный сахарок над сишной древностью.

К тому же меня мало волнуют твои оправдания. Ты зачем-то мне рассказываешь что-то про классы, сравниваешь свой мусор с классами. На каком основании? Если требуешь классов с меня - показывай сам.

Ты долго будешь вертеть жопой чтоб не признавать, что твой сраный полиморфизм не решит нормально проблему отсутствия sum type?

У тебя их нет. Какую такую проблему? Ты мне её покажи.

Слив засчитан, лошара. Извини, писать тебе очевидную реализацию сохранения кружков в xml я не буду

Иди пиши, ты требовал. Зачем мне твои слёзы?

А теперь не верти жопой и пили мне код для сохранения Shape* ptr в xml. Непосредственно код для записи можешь скипнуть, мне достаточно чтоб было ясно куда вставлять «тут сохраняем круг»,

Вот пиши, а потом что-то требуй с меня.

Именно поэтому и придумывали всякие liskov-принципы, чтоб это «всё работает» ногу не отстрелило.

Это мусор ненужный. Существует во всяких примитивных языках не могущих в полиморфизм/типизацию. Вернее «liskov» - это штамп пропаганды.

Это примерно так же как ты примитивный сахарок над сишной древностью называешь сумтипами. Типичный пример новояза. Взять то, что итак было/очевидно и назвать новым именем.

4.2

Уровень аргументации поражает воображение. Вариадики покажешь?

Видишь, всё что нужно было - предложить тебе примитивнейшую задачу сериализации существующей плюсовой иерархии в новый формат, и ты поплыл.

Никакой задачи нет. Ты мне её на расте для начала покажи.

right_security
()
Ответ на: комментарий от khrundel

Тут достаточно других маркеров: днище, мусор, чушь, ненужно, фантазии, съезжаешь, оправдываться и пр. Да и сам стиль изложения + категоричность.

Может не стоить кормить болезного?

eao197 ★★★★★
()
Ответ на: комментарий от khrundel

Зачем тебе пользоваться этим отдельно?

Действительно, зачем. Я понимаю что там в пхп твоём о том что такое удобство/производительность не знают.

Как мне добавить метод к линии? Как мне потом использовать линию?

Нет, в некоторых случаях, конечно, такая необходимость бывает

Во всех.

но в большинстве случаев, как в данном примере с линией, 2 точки тебе достаточны.

Куда мне эти две точки? Что мне с ними делать? Как мне создать линию?

А для круга - точка и радиус.

Ты показывай, а не сказки рассказывай.

То, что ты привык писать огромный класс для линии - это проблема твоих плюсов

Какой класс? Что ты несёшь?

тебе там нужен конструктор

Опять врёт. Никакой конструктор ненужен.

а потом раз уж взялся, напишешь пару операций, чтоб твой класс описывал объект «линия», потом придётся добавить виртуальную функцию для сохранения в xml.

Какую виртуальную функцию? Откуда ты её родил.

Раст отказывается от этой идеи.

Раст ни от чего не мог отказаться. Раст - это примитивный сахарок. Он просто не осилил.

Данные есть данные, 2 точки так 2 точки, незачем городить кучу мусора.

Ну дак ты её и городишь. Показывай пример.

Ссаными тряпками тебя надо гнать из секты фанатиков плюсов. Какая нахрен рефлексия? Это ж нарушение инкапсуляции.

Ты не знаешь что такое инкапсуляция. Зачем позоришься?

Какой в этом смысл? У тебя перегрузка головного мозга. В C написали бы

Ты уже начал оправдываться?

твои божественные плюсы просто избавили от необходимости писать префикс функции.

Зачем ты врёшь. Избавили от необходимости писать префикс - это твой сахарок-огрызок, который трейты. К цпп это отношения не имеет.

А вот простейший вариант «взять массив шейпов и сохранить в xml, не засирая при этом все классы» чистые плюсы уже не осилили, сначала попытались через динамик каст, потом плюнули и добавили вариант, но так как матча не было, а был визитор, пришлось мучиться с разграничением по типу. Получился вот такой кадавр, хранит варианты по индексу, извлекает типу.

Ещё раз. Меня не интересуют твои оправдания. Хочешь какой-то пример - показывай код, а не бойся. Зачем ты боишься? Ты понял своё положение и теперь боишься показать мне код?

right_security
()
Ответ на: комментарий от khrundel

Удивительный в своей несостоятельности персонаж. Как обычно, конечно.

Вместо кода родил подобный мусор:

enum Shape{
  Line(...)
  Circle(...),
}


fn WriteToXml(Shape shape) {
  match shape {
  ...
  }
}

Начал что-то требовать. Ему сообщили, что в том мусоре, что он выдаёт за код - предмета нет. Ему так же параллельно указали на несостоятельность попыток.

Далее он понял, что всё плохо и ушёл в отрицание. Код отказывается давать, с меня требует «повторить» то, чего нет.

Далее с ним произошла типичная для всех подобных персонажей ситуация. Он понял, что целиком и полностью проиграл. И далее начал, брызжа слюной, фанатично выдавать свои фантазии за реальность.

Он где-то пропаганду набрался, а далее когда-то что-то не соответствует пропагандистским штампам - он тут же теряется и начинается отрицать мои доводы, навязывая мне свои фантазии.

Какие-то виртуальные методы, какой-то хмл и прочий пхп. Какие-то указатели и прочие проявление невежества. Какие-то «нужны конструкторы». Понимания нет, даже понимания раста ноль. Но фанатизм - всё.

Ну вот я даю тебе Shape* ptr.

Вот типичный пример. Он понимает своё положение, положение проигравшего. Поэтому он пытается навязать мне свои фантазии, свои штампы. Что он там мне что-то даёт. Что он может что-то делать.

А вот в ситуации со своими фантазиями - он уже решает что и как ему нужно/ненужно.

right_security
()
Ответ на: комментарий от KivApple

Почему бы просто не делать panic в таком случае?

impl AssetLoader {
    pub fn load_shader_program<T: ShaderProgramWrapper + 'static>(
        &mut self, 
        vertex_shader_file_name: &'static str, 
        fragment_shader_file_name: &'static str
    ) -> Rc<T> {
        self.shader_programs
            .entry((vertex_shader_file_name, fragment_shader_file_name))
            .or_insert_with(|| {
                Rc::new(T::new(ShaderProgram::new(&[
                    &self.load_vertex_shader(vertex_shader_file_name),
                    &self.load_fragment_shader(fragment_shader_file_name)
                ]).unwrap()))
            })
            .clone()
            .downcast()
            .expect("failed to downcast")
    }
}

numas13
()
Ответ на: комментарий от KivApple

Ну эта защита не работает тогда. Т.к. ты дважды вызовешь загрузчик с разным типом и у тебя два разных шейдера загрузятся и будут крутиться в мапе. Учитывая, что такое поведение не предусмотрено, это бага, которую будет сложно найти, когда она выстрелит. В то время, как ключ без типа подсветит проблему при первом же неправильном использовании

Aswed ★★★★★
()
Ответ на: комментарий от khrundel

Не зря я зашел в «показать скрытые». Отвечу на то, на что Роман, по моему мнению, ответил неполно.

Человек расстраививался из-за плохого матча, который, по его мнению

Что в такой реализации плохого? Я вижу 2 повода для расстройства

Цепочка if вместо лукапа по таблице

Потенциально долгая операция is.

Оба эти повода для расстройства нелепы, так как enum - это tagged union с целочисленным дискриминантом. Ну а то, как компилятор решит этот switch реализовать, цепочкой проверок ли, или лукапом, или даже двоичным поиском - это дело компилятора.

Боже, до тебя так и не дошло. Повторяю еще раз, мне глубоко наплевать, как там в итоге будет реализован диспатч, я семантику его сравниваю с семантикой ряда if’ов. Синтаксис, кстати, тоже практически аналогичен.

требует внедрения «клиентского» кода во все классы

Не требует. Если есть такое желание, можно написать для твоего Identifier

void print(const Identifier& x) {
    std::visit(overloaded {
        [](Id id) { print(std::to_string(id.value)); },
        [](const Name& name) { print(name.name); },
        [](const Uuid& uuid) { print(uuid.to_string()); }
    }, x);
}

А можно добавить еще один обработчик [](auto && x){ print(x); }, и бесплатно получить возможность добавлять новые типы в Identifier, добавляя их печать лишь если она для них не была реализована.

Через какое-то время даже до тебя дойдет, что портянка из обработчиков не скейлится, в отличие от добавления перегрузок/специализаций, но на это время и так сгодится.

Если мне надо рисовать эти шейпы в разных графических библиотеках

То ты не сможешь ничего сделать. Эти шейпы не примет никакая графическая библиотека, потому что она о них ничего не знает. Тебе придется делать что-то типа

use some::graphics::library::Drawable;

impl Drawable for Shape {
    fn draw(shape: Self, ...) {
        match shape {
            // ...
        }
    }
}

По остальным пунктам я полностью солидарен с @right_security.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium

Следует ещё добавить то, что вот это:

void print(const Identifier& x) {
    std::visit(overloaded {
        [](Id id) { print(std::to_string(id.value)); },
        [](const Name& name) { print(name.name); },
        [](const Uuid& uuid) { print(uuid.to_string()); }
    }, x);
}

Это специальный подложный кейс. Т.е. мы пытаемся воспроизвести убогие костыли из всяких примитивных языков, но мы не можем сделать их настолько позорными.

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

А пустых языках кроме раста - остаётся только верить и навязывать другим свой фанатизм. Отрицая очевидное.

right_security
()
Ответ на: комментарий от Siborgium

Очень хорошо видно проблему на примере ts.

Ты пишешь <T>(x) {} - так нельзя. Тебе приходится бахать интерфейс, но интерфейс требует все типам ему соответствовать. Да, это уже куда лучше того что есть в примитивном расте, но всё равно проблема.

Ты хочешь взять два интерфейса, допустим. Пишешь <T: A|B>(x) {}, но далее ты не можешь заматчить один из них.

Чтобы решить эту проблему - тебе приходится добавлять теги. Но эти теги, конечно же, тайплевел. А не тот мусор доисторический из раста, который ничего не знает о типах. И они хоть удобно работают.

т.е. видно, что это пытались сделать не позорным. Хоть и костыльным. Это можно как-то конкурировать. Проблема только в том, что это и ненужно в цпп.

Это совершенно другой подход, который невозможно понять не пробуя. По крайне мере для тех, кто уже отравлен. Поэтому споры все эти слишком условны. ФАнатизм там всё равно на пером месте.

Удивительно, кстати, как он боится что-то ответить про ts. Все мои упоминания ts он проигнорировал. Наверное на него нельзя так сильно врать как про цпп.

right_security
()
Ответ на: комментарий от Siborgium

Боже, до тебя так и не дошло. Повторяю еще раз, мне глубоко наплевать, как там в итоге будет реализован диспатч, я семантику его сравниваю с семантикой ряда if’ов. Синтаксис, кстати, тоже практически аналогичен.

Ты что-то путаешь. И синтаксис и семантика скорее похожи на switch. За исключением контроля полноты, которую так же предлагает match. Сейчас будешь свистеть что switch в свою очередь тоже похож на цепочку ифов?

Более того, твой «оверлоадед» как раз неудачная попытка косплеить матч. Давай ради прикола в твоём коде заменим visit(overloaded {…}) на несуществующий в плюсах матч, и сделаем более похожим на существующий уже switch.

void print(const Identifier& x) {
    match(x) {
        [](Id id) { print(std::to_string(id.value)); },
        [](const Name& name) { print(name.name); },
        [](const Uuid& uuid) { print(uuid.to_string()); }
    }
}

Видишь, практически та же конструкция, просто её наколхозили как смогли.

Так как так получается, что то что в плюсах отлично, в расте вдруг считается уродством?

Не требует. Если есть такое желание, можно написать для твоего Identifier

Ты полностью упустил суть.

std::variant появился когда, в 2017 стандарте? А как до этого предполагалось решать? Я тебе расскажу. До 98го стандарта это было бы указатель на базовый класс, виртуальная функция и переопределение в дочерних классах. Тот самый случай, который я и описал, так в оригинальных плюсах предлагалось решать проблему с обработкой гетерогенной коллекции. Если коллекция была не твоя, то для тебя был паттерн, вроде «мост» называется, когда ты сбоку повторяешь существующую иерархию типов и оборачиваешь в неё оригинальные. Отличное просто решение.

После 98го добавили RTTI чтоб ты не засирал классы своей новой функцией и не строил вторую иерархию типов. И тогда это как раз решалось твоей любимой цепочкой

if (x is A) {
...
} else if (x is B)

только вместо короткого и в чём-то красивого is у тебя был бы dynamic_cast<A>(). Это, кстати, меня и поражает, насколько надо было быть упоротым, чтоб попрекать раст несуществующей цепочкой ифов, при этом защищая язык, где эта цепочка ифов была во всей красе ещё недавно единственным способом решить проблему определения реального типа? И не то что была, до сих пор есть, чтоб пользоваться более-менее нормальным visit(overloaded ...) тебе нужно чтоб у тебя были данные в виде варианта. Но ты же в своём даже не сильно легаси-проекте из 2014 не можешь такой рефакторинг себе позволить, так что ты продолжаешь писать цепочку if’ов.

А ещё у цепочки ифов есть такое свойство: если в описанном выше примере B является наследником A, то всё перестанет работать. Поэтому отчасти в книжках тебе пишут, чтоб не смотря на возможность наследоваться от кого угодно, ты бы лучше разделял классы на абстрактные интерфейсы и наследуемые от этих интерфейсов реальные классы. А если тебе в B нужно что-то из A, то ты бы лучше положил внутрь объект A в приватное поле.

А можно добавить еще один обработчик [](auto && x){ print(x); }, и бесплатно получить возможность добавлять новые типы в Identifier, добавляя их печать лишь если она для них не была реализована.

Мда… Ссаными тряпками надо тебя гнать из профессии за такие советы. Ты, ради того чтоб облегчить добавление нового варианта (!), закладываешь мину под того, кто будет этот код через пару лет поддерживать. И мина рванёт как раз в случае если потребуется добавить новый вариант.

Да, ему не придётся добавлять строчку [](SomeNewType & x){ print(x); },, но он тебе будет по гроб жизни благодарен за такую помощь, когда в где-то в забытом файле в коде какого-то плагина, работающего только на проде, вдруг втихую начнёт твориться какая-нибудь дичь просто потому, что поток с идентификатором типа SHA256 упадёт в ветку, ожидающую GUID, который вызывает что-то типа тустринг и потом по индексам в строке вырезает куски. А у нового типа идентификатора как раз метод тустринг тоже есть, и добренький C++ послушно подставит в шаблончик новый тип.

То ты не сможешь ничего сделать. Эти шейпы не примет никакая графическая библиотека, потому что она о них ничего не знает. Тебе придется делать что-то типа

Ээээ… да. Ты не поверишь, но чтоб добавить вывод в графическую библиотеку придётся писать код. Никто не обещал, что он напишется сам. И да, код будет выглядеть именно так как ты написал. Он будет локален, не надо будет искать обработчики разных вариантов по 10ку файлов, не потребует добавления методов в чужие классы, и если тебе захочется дропнуть поддержку этой библиотеки - ты просто удалишь 1 или несколько файлов из проекта, и никаких ненужных хвостов нигде не останется.

Ты в чём-то уникум: ты научился пользоваться std::variant так и не поняв, нахрена его вообще добавили.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Ты что-то путаешь. И синтаксис и семантика скорее похожи на switch. За исключением контроля полноты, которую так же предлагает match. Сейчас будешь свистеть что switch в свою очередь тоже похож на цепочку ифов?

Нет, ты всё перепутал. Знаешь что такое pm о котором ты расскажешь и который ты компастил? Это именно функции.

И уж тем более ни на какой свитч это непохоже.

Более того, твой «оверлоадед» как раз неудачная попытка косплеить матч.

Нет, ты даже не знаешь что это такое. Это композиция лямбд. У тебя нет ни лямбд ни композиции.

Никакого косплея матча там нет. Твоя матч - это примитивный мусорный сахарок. Там нечего косплеить. Там даже матча то нет настоящего.

Давай ради прикола в твоём коде заменим visit(overloaded {…}) на несуществующий в плюсах матч, и сделаем более похожим на существующий уже switch.

Нет - это мусор. Ты всё перепутал. Это твой матч - копипаста overloaded, а не наоборот.

void print(const Identifier& x) {
    match(x) {
        [](Id id) { print(std::to_string(id.value)); },
        [](const Name& name) { print(name.name); },
        [](const Uuid& uuid) { print(uuid.to_string()); }
    }
}

Давай разберём твои попытки, неудачные.

void print(const Identifier& x) - мусор из скриптухи не могущей в поилморфизм. Такой сигнатуры быть не может быть.

match(x) { - вообще мимо. Это убогий сахарок не применим здесь.

[](Id id) { print(std::to_string(id.value)); }, - это expr, а не твои фантазии. Они полиморфны. Твой мусорный сахарок в этом не может.

auto x = [](Id id) { print(std::to_string(id.value)); }

overload{
  x,
  [](const Name& name) { print(name.name); },},
  [](const Uuid& uuid) { print(uuid.to_string()); }
}

Я тебе даже больше скажу - можно так:

auto x = [](Id id) { print(std::to_string(id.value)); }

overload y{
  [](const Name& name) { print(name.name); },},
  [](const Uuid& uuid) { print(uuid.to_string()); }
}

overload xy{x, y};
visit(v, x);
visit(v, y);
visit(v, xy);

Ненужно путать свои жалкие пародии на overload с оригиналом.

Видишь, практически та же конструкция, просто её наколхозили как смогли.

Нет, у тебя матч - это пародия на пародию. Есть матч в фп - это попытка сделать overload из крестов. Есть твой матч - это мусорный сахарок, который косплеит матч из фп. А оригиналом в фп является именно композиция функция, т.е. overload.

Так как так получается, что то что в плюсах отлично, в расте вдруг считается уродством?

Да, потому что в расте мусорная пародия на пародию. И то не полноценная а в виде убого сахарка.

right_security
()
Ответ на: комментарий от khrundel

std::variant появился когда, в 2017 стандарте?

Чушь. Вариант существовал всегда в C++ by-design. Ты просто перепутал свой примитивный сахарок и «язык» с цпп и фичами. Где тебе нужно тащить в язык какой-то сахарок, чтобы хоть мочь быть похожим на пародию на пародию.

std::variant - это лишь реализация того, что итак было. Какие-то слабые заходы.

А как до этого предполагалось решать?

Что? Это композиция лямбд. Это вариация:

struct {
    operator()(Id id) { print(std::to_string(id.value)); }
    operator()(const Name& name) { print(name.name); }
    operator()(const Uuid& uuid) { print(uuid.to_string()); }
  } foo;

Ты хоть бы изучил что-то перед тем как позориться. Эта конструкция была ещё в цпп98, когда твоего раста в принципе не было.

Я тебе расскажу. До 98го стандарта это было бы указатель на базовый класс

Нелепая чушь. Заучил какой-то лозунг и повторяет его. Уровень экспертизы я показал выше - она на нуле.

виртуальная функция и переопределение в дочерних классах.

Ты не знаешь ни что такое виртуальная функция ни зачем она нужна.

Тот самый случай, который я и описал, так в оригинальных плюсах предлагалось решать проблему с обработкой гетерогенной коллекции.

Ты повторял какие-то протухшие методички из 90? А когда не фортануло - ты начал рассказывать о том, что было в 90? Нет. Даже тогда такого не было.

Не говоря уже о том, что ссылки на 90 - это уже слив.

Если коллекция была не твоя, то для тебя был паттерн, вроде «мост» называется, когда ты сбоку повторяешь существующую иерархию типов и оборачиваешь в неё оригинальные. Отличное просто решение.

Опять, берём какие-то рандомные лозунги из интернета без понимания что они делают и зачем. Далее выдаёт за проблему. Напоминаю - проблему из 90.

После 98го добавили RTTI чтоб ты не засирал классы своей новой функцией и не строил вторую иерархию типов. И тогда это как раз решалось твоей любимой цепочкой

Опять какие-то фантазии. Какие-то «кресты до 98 года». А чё не раст до 98 года?

right_security
()
Ответ на: комментарий от khrundel

только вместо короткого и в чём-то красивого is у тебя был бы dynamic_cast().

Причём тут dynamic_cast? Ты не знаешь что это такое.

Это, кстати, меня и поражает, насколько надо было быть упоротым, чтоб попрекать раст несуществующей цепочкой ифов

Продолжает врать про какую-то цепочку, хотя ему сказали, что он всё неправильно понял. Но он продолжает врать. Никто ни о каких ифах ему не говорил.

при этом защищая язык, где эта цепочка ифов была во всей красе ещё недавно единственным способом решить проблему определения реального типа?

Какого типы, что ты несёшь? Какого недавного времени? Ты врал про overload, хотя это лишь импрувнутая версия того, что было в 98году как минимум.

Туда же и вариант. Ему тысячи лет. Единственное, что до 0x не было вариадиков. Но они уже лет 15 есть. Тогда никакого раста не было.

Да и его можно накостылить на рекурсии, как сейчас сделан тапл. И как в мусорном расте сделано всё( вернее там даже рекурсия не помогает).

И не то что была, до сих пор есть, чтоб пользоваться более-менее нормальным visit(overloaded …)

Нет, зачем врёшь? Ты даже не знаешь что такое overloaded. Зачем позоришься, повторяю Ему ненужен никакой вариант. В этом и фишка.

тебе нужно чтоб у тебя были данные в виде варианта.

Выше - это враньё.

Но ты же в своём даже не сильно легаси-проекте из 2014 не можешь такой рефакторинг себе позволить, так что ты продолжаешь писать цепочку if’ов.

Да, тут совсем уже следует бригаду вызывать. Случилась методичку от легаси. Сообщаю новость - в 14 году уже практически десять лет был 0x, в котором всё было. И до него было.

К тому же, на каком основании ты в принципе ссылаешься на какое-то легаси, если сравнивает цпп с растом? Это сразу слив.

А ещё у цепочки ифов есть такое свойство: если в описанном выше примере B является наследником A, то всё перестанет работать. Поэтому отчасти в книжках тебе пишут, чтоб не смотря на возможность наследоваться от кого угодно, ты бы лучше разделял классы на абстрактные интерфейсы и наследуемые от этих интерфейсов реальные классы. А если тебе в B нужно что-то из A, то ты бы лучше положил внутрь объект A в приватное поле.

очередной поток бреда. Это базовое свойство иерархии. Какие-то мусорные книжки. Какакие-то прочие нелепые фанатазии. Какой-то абстрактный маня-интерфейсы, который никак не решает описанное выше.

Просто пастит рандомный бред из интернета.

right_security
()
Ответ на: комментарий от khrundel

Мда… Ссаными тряпками надо тебя гнать из профессии за такие советы. Ты, ради того чтоб облегчить добавление нового варианта (!), закладываешь мину под того, кто будет этот код через пару лет поддерживать. И мина рванёт как раз в случае если потребуется добавить новый вариант.

Типичный оправдания. И типичные фантазии про «рванёт». Никакой связи с реальностью не имеющие.

Да, ему не придётся добавлять строчку [](SomeNewType & x){ print(x); },, но он тебе будет по гроб жизни благодарен за такую помощь, когда в где-то в забытом файле в коде какого-то плагина, работающего только на проде, вдруг втихую начнёт твориться какая-нибудь дичь просто потому,

Можно меньше всякого поноса?

что поток с идентификатором типа SHA256 упадёт в ветку, ожидающую GUID

Какие образом? Никаким. На этом можно закончить. Не знаю нужно ли тут что-то комментировать, хотя.

Выше показывалась ДЕФОЛТНАЯ ВЕТКА, дефолтная ветка на то и дефолтная, что она ожидает дефолт. И всё попадут туда, куда нужно.

Самое интересно, что данный персонаж заврался настолько, что подобное поведение так же работает с _ => в расте, где будет тоже самое.

Такие образом персонаж отрицает дефолтную ветку в матче в расте и сообщает, что таких нужно гнать из профессии. Теперь будет наблюдать как он пойдёт это сообщать в юшьюсах в rustc, либо опять сольётся?

который вызывает что-то типа тустринг и потом по индексам в строке вырезает куски. А у нового типа идентификатора как раз метод тустринг тоже есть, и добренький C++ послушно подставит в шаблончик новый тип.

Это очередные потоки бреда не имеющие смысла. Ну подставит - дальше что? Как в расте подставит дефолтную ветку.

Код собрался -код корректен. С++ это не мусорный раст. В нём ненужны никакие мусорные тустринки - разве что в скриптуха-стайл си с классами. Но там своя атмосфера. Там мусорный раст никак не поможет. И если им предложат мусорный матч - они только посмеются.

Ээээ… да. Ты не поверишь, но чтоб добавить вывод в графическую библиотеку придётся писать код. Никто не обещал, что он напишется сам.

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

И да, код будет выглядеть именно так как ты написал.

Т.е. выглядеть мусором?

Он будет локален, не надо будет искать обработчики разных вариантов по 10ку файлов, не потребует добавления методов в чужие классы, и если тебе захочется дропнуть поддержку этой библиотеки - ты просто удалишь 1 или несколько файлов из проекта, и никаких ненужных хвостов нигде не останется.

Как быстро сменил методичку. То вдруг match круто, хотя искать по 10 файлам нужно. То теперь уже ненужно. Ты уж определись.

Удачи удалить новую ветку из match путём удаления «пары файлов».

А знаешь почему в либе трейты,а не твой мусорный enum/match. Потому что люди это знают, а ты просто пропагандист. Который меняет показания по 10 раз за пост.

Ты в чём-то уникум: ты научился пользоваться std::variant так и не поняв, нахрена его вообще добавили.

Ога.

right_security
()
Ответ на: комментарий от khrundel

Видишь, практически та же конструкция, просто её наколхозили как смогли.

Не вижу. Замени я const Identifier на любой другой variant-like тип – код продолжит работать. Добавь я auto&& обработчик – код продолжит работать, и даже лучше, чем сейчас. Потому что это полиморфный код. match – нет.

Так как так получается, что то что в плюсах отлично, в расте вдруг считается уродством?

Этот пример, и пример ранее с a_like и b_like – это один и тот же visit, и один и тот же variant. visit позволяет писать полиморфный код, позволяет писать неполиморфный. match – только неполиморфный. Смекаешь?

std::variant появился когда, в 2017 стандарте? А как до этого предполагалось решать? Я тебе расскажу.

А до этого предлагалось использовать Boost.Variant. Стандартизация std::variant никакого значения не имеет, она ничего не добавляет в сам язык – это чисто библиотечное дополнение.

После 98го

Никого не волнует, что было после С++98, потому что после С++98 не было Rust. Если ты сравниваешь с Rust, сравнивай как минимум с С++14, а лучше С++17. Цепочки is тогда были индустриальным стандартом.

А ещё у цепочки ифов есть такое свойство

А еще у адептов Rust есть такое свойство: писать очевидные вещи с таким видом, будто собеседник о них не знает. Со стороны даже кажется, будто сам адепт разбирается в теме.

Ты, ради того чтоб облегчить добавление нового варианта

«облегчить» к теме отношения не имеет. Речь идет о том, что print пишется единожды, и потом просто работает, а вот каждый match придется дополнять новым вариантом.

закладываешь мину под того, кто будет этот код через пару лет поддерживать. И мина рванёт как раз в случае если потребуется добавить новый вариант.

Я открою тебе секрет: вся библиотека итераторов, а также многие другие крестовые библиотеки работают именно так. Весь <algorithm>, например. И надо же, «мины» пока не рванули – за десятилетия промышленного использования языка. stdlib не пришлось переписывать ежедневно, добавляя новые варианты.

Ты не поверишь, но чтоб добавить вывод в графическую библиотеку придётся писать код.

Знаешь, почему я написал про трейт? Потому что ни одна графическая либа не принимает enum. Трейт позволяет организовать уровень полиморфизма, аналогичный абстрактным классам и интерфейсам – т.е. семантически динамический полиморфизм, который потом кодоген будет превращать в статический.

А теперь поставим мысленный эксперимент. Твой Shape реализует Drawable для M графических библиотек, имея N вариантов. Сколько веток придется дописать, добавь я N+1-й вариант? Ты там ранее что-то про удобство сообщал, не так ли, про локальность?

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Не вижу. Замени я const Identifier на любой другой variant-like тип – код продолжит работать.

Чё ты несёшь вообще? Тебе продемонстрировали, что твоё красивое решение есть по сути попытка повторить матч библиотечными средствами, а ты возражаешь, что это неправда, потому что ты можешь наколхозить свою реализацию variant. Да, ты можешь. Учитывая, что оригинальный написан в библиотеке. Но как это опровергает, что пытались изобразить именно матч?

Добавь я auto&& обработчик – код продолжит работать, и даже лучше, чем сейчас. Потому что это полиморфный код. match – нет.

Ты вообще дурак что ли? Тебе объяснили, что твой auto&& по сути говно, а ты как баран настаиваешь на том, что с ним всё становится лучше.

Этот пример, и пример ранее с a_like и b_like – это один и тот же visit, и один и тот же variant. visit позволяет писать полиморфный код, позволяет писать неполиморфный. match – только неполиморфный. Смекаешь?

Уже возразил, твоё a_like и b_like - это то как писать нельзя ни при каких обстоятельствах. И, самое смешное, ты ведь и сам это знаешь. Знаешь на чём ты прокололся? На принте. Print - это такая эталонная noop функция, про которую точно известно что она ничего не сломает. Будь в твоём примере какой-то полезный код, то сразу бы возникли вопросы типа «а почему ты предполагаешь, что за ресолвом новых типов идентификаторов надо обращаться именно сюда?»

А до этого предлагалось использовать Boost.Variant. Стандартизация std::variant никакого значения не имеет, она ничего не добавляет в сам язык – это чисто библиотечное дополнение.

Да-да-да, началось верчение жопой на тему «а вот васян предлагал что-то подобное ещё в 1917 году». Нет, не прокатит. Если бы boost::variant был достойной альтернативой std::variant, не было бы необходимости добавлять это в std. Видимо у кого-то есть понимание, что не все подключают буст, а вариант - это такая базовая функциональность, которой не место в левой библиотеке.

По существу возражения есть? Или максимум на что надеешься - отодвинуть границу появления варианта на несколько лет в прошлое?

Никого не волнует, что было после С++98, потому что после С++98 не было Rust. Если ты сравниваешь с Rust, сравнивай как минимум с С++14, а лучше С++17.

А мне знаешь, совершенно плевать на справедливость по отношению к C++. Вероятно COBOL в своё время был вполне современным языком, но сравнивая его с чем-то современным я не буду делать скидку на возраст. Факт остаётся фактом: свойство переменной «может быть типа X, а может быть типа Y» в плюсах изначально предполагалось решать через ссылку на базовый класс и полиморфизм. Более того, предполагаю, что в реальном коде этот самый вариант встречается очень редко, отчасти потому что проект старый и тут принято писать вот так, отчасти потому что старпёры, пишущие на плюсах, как-то в своё время освоили 98 стандарт и продолжают писать в нём. Вариант + визит + оверлоадед, который, кстати, не std::overloaded, так что по факту костыль в несколько строк, непонятный для старых программистов. Который ещё и не правильно работает для

overloaded {
[](const std::string &){...}
...
[](auto&&) { ... }
}(something.to_string())

Цепочки is тогда были индустриальным стандартом.

Т.е. странные цепочки is уже не так плохи, когда выяснилось, что они есть в плюсах и их нет в расте?

А еще у адептов Rust есть такое свойство: писать очевидные вещи с таким видом, будто собеседник о них не знает. Со стороны даже кажется, будто сам адепт разбирается в теме.

Деда, ты, видимо, забыл где находишься. Ты находишься посреди спора о том, где реализация sum type сделана по-человечески, а где через жопу. В такой ситуации то что ты сам знаешь о недостатках защищаемого тобой языка, но при этом надеешься, что никто о них не вспомнит - это не то чем следует гордиться. Ты, фактически, признался, что всё это время врал.

«облегчить» к теме отношения не имеет. Речь идет о том, что print пишется единожды, и потом просто работает, а вот каждый match придется дополнять новым вариантом.

Я пишу «облегчить, чтоб не надо было добавлять 1 строчку кода в матч». Ты возражаешь «нет, не облегчить, а просто работает, не нужно добавлять строчку кода в матч». Возникает вопрос: ты что, идиот? Ты не видишь, что твоё возражение повторяет моё утверждение?

Я открою тебе секрет: вся библиотека итераторов, а также многие другие крестовые библиотеки работают именно так. Весь , например.

Спасибо за тайные знания, о гуру. Проблема в том, что я воспринимаю это не как достоинство. Да, в плюсах всё сделано через жопу. Да, плюсы самопротиворечивы, с одной стороны у них чёткое наследование с обязательной пометкой виртуальных функций, с другой - полная утиная типизация в шаблонах. Так и живём.

Знаешь, почему я написал про трейт?

Потому что ты сам не понимаешь что несёшь? Ну т.е. я увидел твой трейт, решил что «ну так тоже можно, а можно и без него, но чё я буду докапываться до ерунды», а оказывается именно в трейте-то и был глубинный смысл. Эвона как. Ок, для прикола:

impl Shape {
    fn draw(shape: Self, ...) {
        match shape {
            // ...
        }
    }
}

или

fn draw_shape_to_canvas(canvas: &mut canvas, shape: &Shape)
  match(shape){
    ...
  }
}

Чем этот код без трейта плох?

Потому что ни одна графическая либа не принимает enum.

Да ты что? Серьёзно? Если я напишу енум, то левая библиотека его не примет?

Трейт позволяет организовать уровень полиморфизма, аналогичный абстрактным классам и интерфейсам – т.е. семантически динамический полиморфизм, который потом кодоген будет превращать в статический.

Ты похож на сумасшедшего, который пристаёт к людям на улице с предложением принять в сердце Иисуса.

Да нахрен твой сраный полиморфизм никому не сдался, понимаешь? Ты думаешь авторам раста было сложно добавить перегрузку функций? Да по факту она уже есть, вот с этим твоим «семантическим динамическим» через трейты, а так же в случае дженериков выбирается нужная реализация. Но простую перегрузку не добавили, равно как и конструкторы заменили на функции, потому что конструкторы без перегрузки кривоваты. Знаешь почему? Потому что опыт показал, что это был поворот не туда. Если ты читаешь код и видишь draw(foo,bar), то в общем-то тебе понятно что что-то куда-то рисуется, но если это глючащий код, то может статься очень важно знать, какая из реализаций draw тут вызывается. И нужно смотреть что это за типы, а они может ещё и приводятся к чему. Поэтому авторы раста сказали: раз уж мы не пытаемся косплеить c++, то незачем нам тащить и ошибки его дизайна. Пишите, товарищи, функции с вменяемым названием, чтоб читатель кода сразу видел, что конкретно вызывается.

А теперь поставим мысленный эксперимент. Твой Shape реализует Drawable для M графических библиотек, имея N вариантов. Сколько веток придется дописать, добавь я N+1-й вариант? Ты там ранее что-то про удобство сообщал, не так ли, про локальность?

Я не понимаю сути твоей претензии. Тебя смущает то, что я, добавляя новый тип шейпа, буду вынужден писать код в те функции, которые рисуют шейп? А ты как хотел?

Нет, я не спорю, бывают предметные области, в которых расширяемым должен был бы быть как раз наш шейп. Для таких предметных областей у Раста так же есть всё, это трейты. Однако кроме них, и гораздо чаще, встречаются как раз закрытые типы, в которых 2-5 вариантов и добавление нового либо исключено, либо редко, а используется данный тип много где. Вот и получается, что в плюсах для такого приходилось использовать наследование, а в Расте предусмотрели нормальный tagged union.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Первое:

Если бы boost::variant был достойной альтернативой std::variant, не было бы необходимости добавлять это в std.

Второе:

а вариант - это такая базовая функциональность, которой не место в левой библиотеке.

Вам не кажется, что второе противоречит первому? Не кажется? А зря.

Факт остаётся фактом: свойство переменной «может быть типа X, а может быть типа Y» в плюсах изначально предполагалось решать через ссылку на базовый класс и полиморфизм.

Кем предлагалось? Хотелось бы точную цитату.

А то ведь непонятно, почему в C++ всегда был union. Как и непонятно, как быть с вашим утверждением, если X – это int, а Y – это double.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Вам не кажется, что второе противоречит первому? Не кажется? А зря.

Мне кажется что эти 2 утверждения не противоречат друг другу и не являются синонимами, они как бы просто о разном.

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

Первое о том, что boost вовсе не какой-то мастхэв, который и так везде есть.

Где здесь противоречие?

Кем предлагалось? Хотелось бы точную цитату.

Страуструпом. Цитату искать не буду, читал в википедии утверждение, что даже rtti не добавили в ранние версии языка потому что труп считал это излишеством, перебирать типы, фи.

А то ведь непонятно, почему в C++ всегда был union.

Элементарно, Уотсон: union был в C.

Однако самая первая книжка по плюсам, которую я читал, где ещё объясняли что такое register и auto, про stl и неймспейсы там не было, про исключения просто упомянули что они есть, но описывать не стали, потому что фигня и в компеляторах не поддерживается. Вот эта книженция предназначалась для знающих C и желающих проапгрейдиться до плюсов, оригинальная работа на английском где-то во второй половине 80х вышла. Вот там объясняли, чем cout << smth; лучше printf, и всякое такое. Там была глава где объясняли как плохой union заменить на нормальное наследование и сделать всё по-человечески. Да и вообще, а какую альтернативу ты бы предложил плюсовику пока не было variant? Реально голый юнион что ли?

khrundel ★★★★
()
Ответ на: комментарий от khrundel

это очень важный базовый компонент, который должен быть в языке из коробки

Он и был из коробки, в виде union.

Но он был небезопасен с точки зрения типизации. И потребовалось время и модернизация языка, чтобы получить безопасный по типам variant. На базе как раз boost::variant.

Где здесь противоречие?

В том, что в C++ со временем в стандартную библиотеку добавляет то, что там должно было бы быть, но не было в силу исторически обусловленных причин. И добавляют это, зачастую, за счет уже существующих реализаций. Из того же boost-а. Как это и произошло с variant.

Цитату искать не буду, читал в википедии утверждение, что даже rtti не добавили в ранние версии языка потому что труп считал это излишеством, перебирать типы, фи.

Как ни странно, но RTTI добавили как раз в C++98, т.е. в первую версию стандартизированного C++.

Ну и тут еще вопрос в том как и кто воспринимает слова Страуструпа. Я, например, всегда считал, что Страуструп говорит о случаях, когда требуется обеспечить полиморфное поведение. Т.е. когда у нас есть Shape, который может быть как Circle, так и Rectangle. И здесь перебирать типы, когда можно пользоваться полиморфизмом, несколько странно.

Но есть и другие случаи, когда у нас может быть либо int, либо double. И здесь никаким полиморфизмом не пахнет. Так что да, в таких случаях union вполне себе допустим, особенно в виде std::variant.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Но есть и другие случаи, когда у нас может быть либо int, либо double. И здесь никаким полиморфизмом не пахнет. Так что да, в таких случаях union вполне себе допустим, особенно в виде std::variant.

Не в этом дело. Перебирать «типы» мусор не потому что «перебирать», а по той самой причине почему match параша, которая является такой же иф-лапшой по-сути.

Разницу определяют закрытые/открытые типы. Т.е. логика на ифах дерьма(пусть и даже мусорный раст-сахарок) - она закрытая. Логика на на тех же виртуальных функциях - открытая.

И в этом фундаментальная проблема. Логика на открытых типах - дырявое дерьмо, а вот логика на закрытых типах нет. Но реализации закрытых типов все дерьмо, особенно в таком мусоре как раст. Даже в том, откуда его копипастили. Там хоть и лучше, но всё равно мусор.

  • Чем помогают закрытые типы? Система типов знает о всех вариантах и может давать асист. Конечно же, в мусорном расте ничего этого нет - там система типов не знает ни о каких типах. Там убогий сахарок. Но нормальные закрытые типы дают преимущество. Я приводил выше в пример ts.

  • Чем помогают открытые типы? Это расширяемость. Можно без изменения кода добавлять новые сущности. Допустим, у нас есть ast, где оно крутится-вертится. Мы можем создать новый тип ноды, отнаследовать и всё будет работать.

Раст как пример извечной дыры.

  • Трейты. Это убогая вариация внешних интерфейсов из бородатых годов. Они были созданы как примитивщина, которая решает одну из проблем. После адепты не осилив ничего - спастили это и начали выдавать за что-то состоятельное. Это чушь, конечно же.

Понять это просто на примере всё того же ast. Да, мы можем создать новый интерфейс. Даже обмазать его vtable(вернее оно там всегда есть семантически) и это будет работать, но только на уровне интерфейса. Но толку? Есть наша новая ast-нода должна ещё хранить свои данные? Где она будет их хранить? В астрале?

Вот чтобы решить такое - раст-адепт будет костылить свою убогую вариацию классов/наследования. И это будет полностью неюзабельным мусором.

  • Что нам предлагает адепт? Взять enum-сахарок ака копипасту сишной древности. Всё, если с трейтами у нас была хоть какая-то расширяемость пусть и коду. То вот здесь её нет даже по коду. Не говоря уже о том, что даже без расширяемости - там нет виртуальных функций. Т.е. динамического диспатча.

Данные же тоже хранятся в мусорном union. Таким образом, если у нас есть нода на мегабайт, условно, и все остальные на килобайт - все ноды будут на мегабайт и программа будет жрать в 1k больше памяти.

Как это решает С++

  • Есть нормальное решение классах/виртуальных функциях. Оно дырявое, но оно работает. Это си с классами.

  • Но из си с классами родился новый язык, который породил совершенно новый класс типов.

Полиморфизм позволяет решить проблему расширяемости для закрытых типов. Да, цпп это ещё росток и си с классами говно его пытается задавить.

Полиморфизм обладает всеми преимуществами обоих подходов и нивелирует их недостатки. Он статически проверяемый, он быстрый, он расширяемый. В С++ не все фичи работают, конечно. Но это проблема наследия, а не модели. В любом случае даже сейчас эта модель на голову выше всего(особенно мусорного раст-сахарка).

void impl(a);
void impl(b);
void impl(c | d);

using t = a | b | c | d;

void process() {
  t x = get();
  visit(impl, x);
}

Т.е. мы получаем свойства открытого типа по расширяемости, при этом получаем тот самый «exhaustiveness checking» из раст/фп-методичек.

При этом мы получаем прозрачный интероп с полиморфизмом полноценном, impl(a);impl(a|b|c|d); - всё это будет работать.

Таким образом мы:

  • для компилтайм-вариантов мы получаем полную расширяемость(и по данным и по коду), мы убираем рантайм-диспатч. Ещё множество фичей.

  • Для рантайм-вариантов мы получаем расширяемость по коду, интероп с кт-вариантом на халяву. Да, расширяемость данных мы не можем протащить в рантайм. Придётся закрывать типы руками. Но, повторяюсь, это не проблема модели. Проблема текущей реализации.

right_security
()
Ответ на: комментарий от khrundel

Ясно, ты поломался и просто пытаешься полить меня оскорблениями так, чтобы за это не сняли скор. Не зря я когда-то тебя в игнор закинул, написав пометку «идиот и сектант».

Тебе продемонстрировали, что твоё красивое решение есть по сути попытка повторить матч библиотечными средствами,

Попыткой повторить можно называть реализацию, которая появилась позже и делает меньше или столько же, сколько оригинал. Весь Rust появился позже, чем крестовые variant+visit, а его match при этом может меньше – как я показал ранее.

Именно match является а) захардкорженной б) кривой в) менее функциональной копией. Сравнивать match можно исключительно со switch.

Ты вообще дурак что ли? Тебе объяснили, что твой auto&& по сути говно, а ты как баран настаиваешь на том, что с ним всё становится лучше.

Ты вообще дурак что ли? Тебе объяснили, что твой match по сути говно, а ты как баран настаиваешь на том, что с ним всё становится лучше.

Приведи хотя бы один довод, на который тебе не ответили. Ты уже несколько раз показал свое принципиальное непонимание того, о чем тебе пытаются рассказать я и @right_security, но ты продолжаешь.

Если бы boost::variant был достойной альтернативой std::variant, не было бы необходимости добавлять это в std. Видимо у кого-то есть понимание, что не все подключают буст, а вариант - это такая базовая функциональность, которой не место в левой библиотеке.

@eao197 уже исчерпывающе ответил на этот вопрос. Boost.Variant имел ошибку в дизайне, которая, тем не менее, критической не являлись – и, целиком за вычетом этой ошибки, перешел в стандарт как раз потому, что был востребован. «Ошибка» заключалась в том, что они предпочли аллоцировать в случае появления исключения, чтобы избежать введения пустого состояния.

Вероятно COBOL в своё время был вполне современным языком, но сравнивая его с чем-то современным я не буду делать скидку на возраст.

Что же, даже если не делать скидку на возраст, COBOL может похвастаться промышленным применением в масштабе, не осиленном Rust. Переписывание coreutils таковым не считается, извини.

Факт остаётся фактом: свойство переменной «может быть типа X, а может быть типа Y» в плюсах изначально предполагалось решать через ссылку на базовый класс и полиморфизм.

Скажу страшную вещь, но вариант сам по себе не имеет тип X или Y. Тип относится к субобъекту в сторедже варианта, и с точки зрения языка это разные вещи.

Кроме того, мне лично без разницы, что там и как предполагалось решать. Вот вообще наплевать. Я рассказываю о том, почему match это не аналог visit, и почему visit это гораздо более мощный инструмент. Все.

Print - это такая эталонная noop функция, про которую точно известно что она ничего не сломает

Если бы ты только знал, сколько всего может сломать простой print.

Будь в твоём примере какой-то полезный код, то сразу бы возникли вопросы типа «а почему ты предполагаешь, что за ресолвом новых типов идентификаторов надо обращаться именно сюда?»

Вообще не распарсил предложение. Ты точно понимаешь приведенный ранее код?

Т.е. странные цепочки is уже не так плохи, когда выяснилось, что они есть в плюсах и их нет в расте?

  1. Прямой аналог if (x is Y) для match это if let, match является полным аналогом цепочки is. Я уже описал, почему.

  2. Я не писал про «плохи» или «хороши». Я написал, что они были во всех языках того времени, и поэтому попали и в тогдашний С++ (точнее, С с классами).

Цепочки is не являются полиморфными, потому что по определению обходят закрытое множество типов – как и match. Ты, же, кстати, отрицал соответствие match и цепочек is – случилось что-то?

Ты находишься посреди спора о том, где реализация sum type сделана по-человечески, а где через жопу.

В котором ты проигрываешь. Ты не смог ничего ответить на указанный пример, показывающий, чем модель variant+visit принципиально мощнее модели enum+match, кроме рассказов о том, что это не нужно, потому что это закладывает какие-то мифические мины под мифический прод.

Дальше ты сам подтверждаешь, что в курсе, что указанная модель успешно работает всю историю крестов, почти втрое дольше, чем существует Rust.

Проблема в том, что я воспринимаю это не как достоинство

Реальность не волнуют наши чувства.

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

Не понимаю, каким образом возможность делать x is Y в С++ является недостатком.

Ты, фактически, признался, что всё это время врал.

Это о чем же?

Я пишу «облегчить, чтоб не надо было добавлять 1 строчку кода в матч». Ты возражаешь «нет, не облегчить, а просто работает, не нужно добавлять строчку кода в матч». Возникает вопрос: ты что, идиот?

Возникает такой же вопрос.

Ты не видишь, что твоё возражение повторяет моё утверждение?

Нет, потому что оно не повторяет. Речь идет не о том, чтобы было легче или тяжелее. Речь идет о том, чтобы не добавлять строчку в матч, а, если и потребуется ее добавлять, добавлять ее вне матча – потому что, как я уже написал, добавление строчек в матч не скейлится.

Именно поэтому в Rust все используют трейты – те же самые интерфейсы, только внешние, и никто не использует enum’ы.

Чем этот код без трейта плох?

Да ничем, пропорция не поменялась. Для M канвасов из разных библиотек будешь писать N строчек в каждом матче. Все те же N * M итого.

Siborgium ★★★★★
()
Ответ на: комментарий от khrundel

Да ты что? Серьёзно? Если я напишу енум, то левая библиотека его не примет?

А что, примет?


// main.rs

enum Main {
    A,
    B(String),
    C(u32)
}

// some_lib.rs

enum Main {
    A,
    B(String),
    C(u32)
}

fn foo(m: Main) -> ();

Теперь передавай Main из своего main.rs вместо библиотечного Main в функцию foo. Жду с нетерпением.

Ты думаешь авторам раста было сложно добавить перегрузку функций?

Да. Думаю, что сложно. Я когда-то на Rust писал, в районе 2018. Скоро 2022 закончится, наступит 2023, а вариадиков так и нет, const generics сделали через тупой eval, бч как был тупым, так и остался, за три итерации. Итого за 4 года Rust не вырос ни капли. А знаешь почему? Потому что его делают все те же люди, которые не смогли сделать его вменяемым языком за годы до 2018. У него даже нет своего собственного компилятора, только убогий фронт к LLVM и еще более убогий к GCC. Ну да неважно, речь не об этом.

Если ты читаешь код и видишь draw(foo,bar), то в общем-то тебе понятно что что-то куда-то рисуется, но если это глючащий код, то может статься очень важно знать, какая из реализаций draw тут вызывается.

Да, мне важно. Правда, в

fn foo<T: Drawable>(t: T) {
    t.draw()
}

почему-то не очень понятно, какая реализация draw вызывается. Подскажешь? А то выглядит, будто авторам было глубоко наплевать на понятность.

Все же дополню про сложность: трейт это буквально та самая vtable. Когда у тебя указан трейт, компилятору не нужно ничего определять, у него все уже есть, потому что ты уже все сам указал руками. Перегрузка – это достаточно сложный многоуровневый процесс, поэтому ее сложно реализовать. Ценой сложности дается та самая расширяемость. Код просто работает, для твоих классов не нужно реализовывать никакие трейты, ни от чего наследоваться.

Тебя смущает то, что я, добавляя новый тип шейпа, буду вынужден писать код в те функции, которые рисуют шейп? А ты как хотел?

Добавляя новый тип шейпа ты будешь менять каждую функцию, рисующую шейп, вместо добавления одной сторонней реализации. В реальности даже в Rust для каждого типа шейпа писалась бы отдельная реализация Drawable, потому что enum не работает. В крайнем случае для enum написалась бы Drawable, просто передающая вызов Draw дальше – то самое, с чего мы начинали,

match (x) {
    A(a) => a.draw(),
    B(b) => b.draw(),
    // ...
}

Однако кроме них, и гораздо чаще, встречаются как раз закрытые типы, в которых 2-5 вариантов и добавление нового либо исключено, либо редко, а используется данный тип много где. Вот и получается,

…что каждый раз пишется портянка match. Мы опять возвращаемся в начало, для значений разных типов лучше изначально использовать разные пути исполнения, см. пример полиморфности функции по входным данным.

Вот и получается, что в плюсах для такого приходилось использовать наследование

Еще раз, Boost.Variant существовал с самых древних крестов.

а в Расте предусмотрели нормальный tagged union.

Он ничем не отличается от рукописного, кроме компиляторного сахара. Но еще раз, variant это не замена tagged union, их нельзя сравнивать.

Siborgium ★★★★★
()
Ответ на: комментарий от khrundel

Вариант + визит + оверлоадед, который, кстати, не std::overloaded, так что по факту костыль в несколько строк, непонятный для старых программистов.

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

Который ещё и не правильно работает

Он работает полностью правильно и ожидаемо, просто ты не знаешь С++.

Siborgium ★★★★★
()
Ответ на: комментарий от eao197

Он и был из коробки, в виде union.

Так почему не пользовались? Зачем надо было колхозить std::variant? Я не понимаю сути твоих возражений. Для меня очевидно, что юнион вообще тут никаким боком.

В том, что в C++ со временем в стандартную библиотеку добавляет то, что там должно было бы быть,

Ну как бы и я о том. Siborgium утверждает что никаких проблем нет, variant присутствует в бусте 100 лет уже и соответственно говорить об ущербности плюсов нельзя. Я возражаю ему что

  1. такой тип должен быть из коробки (что подтверждается фактом, что его в коробку добавили)
  2. буст не считается за «из коробки»

Как ни странно, но RTTI добавили как раз в C++98, т.е. в первую версию стандартизированного C++.

Я не испытываю пиетета перед комитетом спиногрызов из ИСО/АНСИ. Для меня писульки Страуструпа не менее важны чем эти же писульки, пропечатанные комитетами.

Ну и тут еще вопрос в том как и кто воспринимает слова Страуструпа. Я, например, всегда считал, что Страуструп говорит о случаях, когда требуется обеспечить полиморфное поведение.

Раз речь идёт о нежелании влкючать такую возможность в язык вообще, нельзя говорить о каких-то особых случаях. Видимо Страуструп считал, что нет таких случаев, когда RTTI необходимо, предлагал обходиться нормальным динамическим полиморфизмом. И в чём-то он, безусловно, прав: в случае если я забуду реализовать чисто виртуальную функцию мне компилятор напомнит. А вот заставить компилятор проверить лапшу из if (dynamic_cast<…>(…)) на полноту вообще невозможно никак.

Но есть и другие случаи, когда у нас может быть либо int, либо double.

Ты будешь смеяться, но вот тот самый пример из книжки как раз такого случая и касался. Понятно, что пример был упрощённый, для демонстрации принципиальной возможности, но всё-таки.

Давай по-честнаку: если бы Труп реально хотел предложить пользователям юнион, он бы его предложил. Он ввёл енумы, деструкторы, операторы преобразования, неявный вызов конструкторов. Он позволил добавить виртуальные функции в структ, в тот же юнион, что лишало их бинарной совместимости с C. Не было никакой причины чтоб он не предоставил опционального метода превращения union в tagged union, если бы такое желание у него было.

khrundel ★★★★
()
Ответ на: комментарий от Siborgium

Ясно, ты поломался и просто пытаешься полить меня оскорблениями так

Извини. То что ты - дурак не даёт права тебя обзывать.

Не зря я когда-то тебя в игнор закинул, написав пометку «идиот и сектант».

Ты или крест сними, или трусы надень. Нельзя одновременно плакаться от того что тебя считают дураком и с гордостью же заявлять, что ты когда-то где-то там в тетрадке записал что считаешь дураком взаимно.

Давай посмотрим правде в глаза: ты написал глупость про цепочку if. Которая, видимо, в плюсах тебя достала. Скорее всего думал, что там реально что-то типа RTTI используется. Потом ты узнал, что в Расте всё не так. Но признать этого не мог, вот и начал вертеться, типа да, непохоже, зато «в душе» (т.е. семантически, ага) оно та же цепочка ифов.

Попыткой повторить можно называть реализацию, которая появилась позже и делает меньше или столько же, сколько оригинал. Весь Rust появился позже

При чём тут Раст? Кто-то говорил, что в Расте матч изобрели что ли? Боюсь ошибиться, но матч вроде в том же Хаскеле есть, который в 90х запилили, да и там, скорее всего, он не первым придуман.

Ты вообще дурак что ли? Тебе объяснили, что твой match по сути говно

Бзззт, тут ошибка. Никто не объяснил почему матч говно. Ты чего-то ныл про то что у тебя он по какой-то непонятной причине вызывает ассоциации с лапшой из if (x is A), но ни объяснить внятно, откуда эта ассоциация взялась, ни почему это плохо ты так и не смог.

Я же тебе вполне внятно объяснил, почему шаблон в матче, да ещё и с утиной типизацией - это лютый п***ц. Возражений не услышал, ну кроме «а в плюсах всё такое же кривое».

уже исчерпывающе ответил на этот вопрос.

Ты настолько обосрался, что стал взывать к авторитету окружающих?

Нет, он не ответил, он даже не понял о чём я собственно писал. Моя вина, видимо, но теперь надеюсь понятно объяснил? Если кратко: буст не в счёт.

Вообще не распарсил предложение. Ты точно понимаешь приведенный ранее код?

Чего сложного в твоём коде? Я этого говна на тему превосходства полиморфизма ещё в универе наслушался.

Ну а раз ты не распарсил, поясню: в твоём примере используется очевидно безопасная функция, если бы вместо принта был бы ресолв, стало бы очевидно, что написал ты хрень, потому что ресолвить разные типы идентификаторов одним и тем же способом явно нельзя, одни данные, например, берутся из файла на диске, другие из базы, третьи по урлу из тырнета.

Поэтому ты включил дурачка и написал для !!!идентификатора!!! не получение идентифицируемого объекта, не непонятный foo, а безобидный print.

Так понятно?

Прямой аналог if (x is Y) для match это if let, match является полным аналогом цепочки is. Я уже описал, почему.

Дай догадаюсь: потому что и там и там if?

Ты писал глупость, за что был осмеян.

Я не писал про «плохи» или «хороши». Я написал, что они были во всех языках того времени, и поэтому попали и в тогдашний С++ (точнее, С с классами).

Ты писал именно что они плохи. Весь смысл твоего наезда был в том, что матч плох, потому что является аналогом вот этой штуки.

Цепочки is не являются полиморфными, потому что

Стоп. А не посрать? Повторяю, ты со своим полиморфизьмом носишься как дурень со ступкой. Всем насрать на твой полиморфизм. Если что-то полиморфно - это не значит хорошо.

Ты, же, кстати, отрицал соответствие match и цепочек is – случилось что-то?

До сих пор отрицаю. Ты написал очевидную глупость, зачем теперь делаешь вид, будто с тобой согласились?

В котором ты проигрываешь.

Пока что я вижу как ты верещишь и с перепугу даже призвал в свидетели Царя.

Ты не смог ничего ответить на указанный пример, показывающий, чем модель variant+visit принципиально мощнее модели enum+match, кроме рассказов о том, что это не нужно, потому что это закладывает какие-то мифические мины под мифический прод.

А этого мало? Давай ещё раз повторю: единственное достоинство, которое ты заявил - это то, что в случае добавления нового варианта компилятор не предупредит тебя о тех местах, где не хватает обработки данного варианта. Каким надо быть идиотом, чтоб продолжать «а зато скомпиляется без ошибок». Вообще, у растохейтеров это общее, вы так ссыте, что компилятор вас обругает, что предпочитаете наличие багов в софте.

Дальше ты сам подтверждаешь, что в курсе, что указанная модель успешно работает всю историю крестов, почти втрое дольше, чем существует Rust.

Где это она успешно работает, болезный? Плюсы, если что, просрали все полимеры, в линупс-ведро их не пустили. Из прикладухи их почти вытеснили, живут за счёт легаси, которое слишком дорого переписывать. Раст, являющийся в чём-то более низкоуровневым чем плюсы, набирает популярность, а плюсы стагнируют. Если хочешь реально оценить успешность модели, посмотри на другие языки, даже те, которые пытаются походить на плюсы. Везде от утиной типизации отказались. Везде разделили базовые классы и интерфейсы. Такова селяви: все поняли, что в дизайне ошибка. В нормальных конторах об этих ошибках знают и стучат по башке тем, кто пытается такое писать.

Реальность не волнуют наши чувства.

В следующий раз когда захочешь повторить крутую фразу, убедись что она подходит по контексту. Какая нахрен реальность в споре с идиотом, которому не нравится матч потому что он «как цепочка ифов»? Это вкусовщина.

Не понимаю, каким образом возможность делать x is Y в С++ является недостатком.

Это в первую очередь у тебя надо спросить. Ты же критиковал матч за мнимую похожесть на неё.

Я со своей стороны отвечу: у цепочки ифов минус в том, что нет контроля компилятора за полнотой проверки. Ты сам же писал, что цепочка ифов, равно как и матч, предназначены для перебора закрытого набора вариантов. При этом матч предупредит тебя, если ты чего забыл, а цепочка ифов - нет. Так понятно?

Нет, потому что оно не повторяет. Речь идет не о том, чтобы было легче или тяжелее. Речь идет о том, чтобы не добавлять строчку в матч, а, если и потребуется ее добавлять, добавлять ее вне матча – потому что, как я уже написал, добавление строчек в матч не скейлится.

Ты решил напоследок дурачка включить? Хорошо, ещё раз повторю: да, существуют, хоть и редко, места, где «динамический полиморфизм» или, для людей, которые понимают что ООП - говно, общий интерфейс, может пригодиться. И в расте, как и в плюсах, для этого есть всё что нужно. И в расте, благодаря возможности имплементить свой трейт для чужого класса, это тоже лучше реализовано.

Но речь не об этих случаях. И std::variant тоже не для этих случаев придуман. Так что не надо с поля, на котором ты проиграл, пытаться незаметно переползти на другое.

Именно поэтому в Rust все используют трейты – те же самые интерфейсы, только внешние, и никто не использует enum’ы.

Уверен что не используют? Можешь дать приблизительную оценку, насколько редко используют енумы в расте? А я посмеюсь.

Да ничем, пропорция не поменялась. Для M канвасов из разных библиотек будешь писать N строчек в каждом матче. Все те же N * M итого.

Ты слышал про процедурное программирование когда-нибудь? Оно позволяет N сократить до минимального необходимого.

И в случае твоего любимого полиморфизма то же самое, те же M*N. А, учитывая многословность плюсов, необходимость писать объявление и реализацию, строчек кода ты больше напишешь. Единственный способ сэкономить - это наследование реализации. А за неё ещё GoF советовали по рукам бить.

khrundel ★★★★
()
Ответ на: комментарий от Siborgium

Да ты что? Серьёзно? Если я напишу енум, то левая библиотека его не примет?

А что, примет?

Так и запишем: про сарказм не слышал.

Естественно не примет, блин, естественно если я запилю свой тип шейпов мне придётся писать код, который будет рисовать их в библиотеке, а как иначе-то?

Да. Думаю, что сложно. Я когда-то на Rust писал, в районе 2018. Скоро 2022 закончится, наступит 2023, а вариадиков так и нет, const generics сделали через тупой eval

Может потому что люди предпочитают подумать перед тем как делать?

бч как был тупым, так и остался, за три итерации.

В смысле тупым? А что он должен был сделать умного? На всякий случай, пока ты не начал фантазировать: БЧ должен действовать одинаково при условии неизменных прототипов функций. Т.е. варианты «ему что сложно посмотреть что вот внутри вызываемой функции» сразу отметаются.

У него даже нет своего собственного компилятора, только убогий фронт к LLVM

Тебя Царь что ли покусал? Будешь рассказывать про ворованое? Так и у плюсов нет своего компилятора, тот же убогий фронт к LLVM и GCC.

Да, мне важно. Правда, в почему-то не очень понятно, какая реализация draw вызывается.

Опять решил дурачка влкючить? Да, имена методов интерфейса обязаны совпадать, без этого никак. И в этом есть смысл, есть интерфейс, заметь, не утиная типизация, а конкретный интерфейс, вероятно документированный, и есть место в котором специально сказано, что нужно вызвать метод данного интерфейса. Но это не повод брать и называть одинаково обычные функции, где никакой двусмысленности на тему «что вызывается» со стороны компилятора нет, один только человек, читающий код, вынужден тратить усилия на ресолв.

Все же дополню про сложность: трейт это буквально та самая vtable. Когда у тебя указан трейт, компилятору не нужно ничего определять

Бугагашеньки. Какая нахрен втейбл? Нет у компилятора втейбла, у него есть название метода и имя переменной, для которой он вызывается. Ему нужно посмотреть что это за тип, а потом выбрать функцию с этим именем у которой селф соответствующего типа. Просто? Да, просто. А теперь берём твой вариант: взять переменную, узнать её тип, взять список реализованных трейтов, найти среди них тот, в котором есть функция с таким именем, если найден узнать индекс во втейбл и взять оттуда, если не найден, взять простые методы. Тоже в общем-то просто, но уже сложнее.

Перегрузка – это достаточно сложный многоуровневый процесс, поэтому ее сложно реализовать.

Вы, растохейтеры, смешные. Свою глупость и неспособность понять, какие причины могли быть у того или иного решения вы объясняете тем, что все вокруг идиоты и не могут решить простую задачу.

Давай дам тебе задачку программистскую. У тебя есть список (до 100) коротких слов (последовательностей букв точнее, буквы в нашем случае обозначают типы параметров). Уникальных. каждое слово содержит некоторое количество букв (от 0). Кроме того у тебя есть несвязанный граф перехода букв одной в другую.

  • Задача на тройку: имея некое слово найти индекс его в списке.
  • Задача на четверку: внутри слов может находиться разделитель, тогда буквы до разделителя обязаны совпадать, а после него совпадать только пока не кончатся буквы в искомом слове.
  • Задача на пятёрку: если точное совпадение не найдено, найти индекс первого слова, которое достижимо трансформацией искомого слова через этот граф с ограниченным количеством шагов трансформаций.
  • Задача на пятёрку с плюсом: найти все такие слова и вывести сообщение «нипонятно» если их слов с минимальной длиной трансформации больше 2х.

Вот серьёзно, сколько времени тебе понадобилось бы на решение этой задачи? Ты правда думаешь, что авторы раста тупее тебя?

А теперь посмотри на сложность растовского вывода типов, когда ты можешь объявить, например, просто вектор неизвестно чего, потом передать куда-нибудь замыкание внутри которого этот вектор будет заполняться чем-то, тип чего тоже явно не объявлен, но может быть выведен из ещё чего-то, и компилятор по этой цепочке пройдёт и выяснит, что в твоём векторе должны лежать данные вот именно такого типа.

Добавляя новый тип шейпа ты будешь менять каждую функцию, рисующую шейп, вместо добавления одной сторонней реализации.

Да-да-да, когда изобретут новый тип шейпа, тогда и приходи.

Ты, дурень, с одной стороны сам мне пишешь про открытые и закрытые множества типов, а с другой всё пытаешься просунуть идею, будто у меня вдруг появится новый тип шейпа, как будто закрытых не существует. Не появится он, скорее всего. Я буду рисовать эти шейпы на экране, выводить на принтер, экспортировать в SVG и ещё кучу других разных форматов. А когда в 100500 релизе мне вдруг приспичит добавить новый шейп, раст мне сообщит и о том, что у меня нет кода, выводящего этот шейп на экран, нет кода, выводящего его в SVG и не отстанет от меня, пока я не исправлю эти недочёты везде. И нет никакого способа тут смухлевать, я не могу, например, взять класс шейп и завести ему виртуальную функцию getName чтоб можно было удобно сохранять все виды шейпов в xml-подобные типы файлов одной веткой кода. Отчасти потому, что толку от этого никакого, если у данного вида шейпа в данном формате имя «star», то в другом это будет 5star или five-star или вообще такого шейпа не будет и мне придётся изображать его ломаной. И никакой visit(overloaded{[](auto&&){}}) тут помочь не может, наоборот, если бы он был, я бы 100% поломал сохранение в какой-нибудь формат.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Вот серьёзно, сколько времени тебе понадобилось бы на решение этой задачи? Ты правда думаешь, что авторы раста тупее тебя?

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

А теперь посмотри на сложность растовского вывода типов, когда ты можешь объявить, например, просто вектор неизвестно чего, потом передать куда-нибудь замыкание внутри которого этот вектор будет заполняться чем-то, тип чего тоже явно не объявлен, но может быть выведен из ещё чего-то, и компилятор по этой цепочке пройдёт и выяснит, что в твоём векторе должны лежать данные вот именно такого типа.

Ты просто не понимаешь тему, но зато хорошо ретранслируешь пропаганду. Правда мусорную, но всё же. И твоё непонимание порождает у тебя магическое мышление, когда каждое непонятно ты отождествляешь с богом.

Я открою тебе тайну, но в расте нет никакого вывода типов. Там есть мусорная мономорфная подстановка типов, которую делают студенты на лабах. Ценность её ноль.

Генерики же мусорные - не являются полиморфными. И не требуют какого-либо вывода. Генерики вообще ничего о типах не знают. И раст там ничего не выводит.

Фокус с лямбдой - вообще мусор, работает за счёт протаскивания шаблонов. Но лишь в мономорфном контексте, потому как лямбда не может быть полиморфной. Опять же уровень студента начальных курсов.

Т.к. генерики являются примитивным мусором - любой генерик с любым типом можно заменить на любой другой.

Точно так же как рассказывать о выводе типов в void foo(base & x) - вот все генерики - это базовые классы. Базовым классом для любого генерика является _, либо сам трейт.

Таким образом любой vec<T> можно свести к vec<_>, потому что T там фейковый. Это не темплейт-параметр, который настоящий. Таким образом никаких типов, никакого вывода типов. Только тупо стирание типов и замены всей типизации на vec<_>, либо на базовый класс.

Вот представь, что есть base_vec, а далее есть template<typename T> vec: base_vec {}; Далее ты все функции пишешь как foo(base_vec & v) Именно так работает раст.

То, что тебе пропаганда выдала как типы - это не типы. Это типовые алиасы. Это работает так же как лайфтаймы, где генерики уже ссылочные алиасы. Это одно и тоже.

Вот когда ты пишешь T - это просто идентификатор, далее ты им аннотируешь какое-то значение. Таким образом скриптуха понимает, что тип значения a, равен значению b.

foo<'a>(a'a) -> any'a - точно так же как с лайфтаймами. Когда ты в этот мусор передаёшь let x = foo(123) - оно тупо знает, что x алиас типовый 123. Всё, ни о каких типах, выводе оно не знает.

Там есть примитивный матчинг, т.е. когда пишешь vec<T> - оно алиасит с ним внутренние значения. А далее если ты сделаешь return vec[0] - он понимает, что любой value в векторе заалиашено через генерик-костыль.

Всё, далее из этого мусора мы получаем некое имея и список его алиасов. Если в списке алиасов есть некий конкретный тип и он один - говно сходится. Нет - типа нет, ошибка. Два типа - ошибка. Ну и так далее.

Всё, весь алгоритм «вывода» - мусор уровня начальной школы. По сложности равен где-то 1/100 от С++, даже в самом базовом виде. При том, что система типов С++ бесконечно скейлится и расширяется.

right_security
()
Ответ на: комментарий от khrundel

Отчасти потому, что толку от этого никакого, если у данного вида шейпа в данном формате имя «star», то в другом это будет 5star или five-star или вообще такого шейпа не будет и мне придётся изображать его ломаной.

Зачем ты так позоришься с мазами уровня начальной школы? Это надо такую чушь придумать, что все типы будут иметь разное имя во всех контекстах. К совести твоей взывать бесполезно, но всё же. Это же максимально смешно.

К тому же, как тебе «убогий сахарок» ака «сишный костыль из древности» поможет? Никак. Но ты можешь показать. Вернее попытаться.

И никакой visit(overloaded{{}}) тут помочь не может, наоборот, если бы он был, я бы 100% поломал сохранение в какой-нибудь формат.

Это ты так сказал? И почему ты боишься показать код? Ты выше начал какие-то огрызки кода показывать, а как только понял что быстро поймают перешёл с кода на фантазии. Фантазировать то можно сколько угодно, да? И никто не поймает.

right_security
()
Ответ на: комментарий от khrundel

Это в первую очередь у тебя надо спросить. Ты же критиковал матч за мнимую похожесть на неё.

Кстати, удивительные масштабы невежества. Ему говорили о том, что матч - это иф-мусор, который матчит в рантайме по числу ничего не зная о типе.

Просто пример - у меня есть два типа. A и B, я сую его в убогий сахарок на сишным костылём из древности. Но оно же ничего не знает о типах - мне придётся добавлять туда убогие теги.

Такие образом вместо if(x is T) я буду обмазывать T тегами, x тегами. Проблема в этом, а не в чём-то иное.

Так же проблема в том, что теги не скейлятся. Поэтому в примере выше с std:Any из раста - используется именно is, а не мусорный match по мусорным тегам.

Самое интересное, что C++ в отличии от этого убого огрызка - позволяет делать внешние теги. Собственно на них вариант и построен. Точно так же как и тапл. При этом это всё работает в компилтайме и на тайплевеле. А ни компилтайма ни тайплевела в этом мусоре нет. Есть только пародия, которая не является таковой, но выдаётся пропагандой за таковую.

right_security
()
Ответ на: комментарий от right_security

Кстати, доказывается мусорность и неспособность к выводу простым тестов. Если раст выводит типы, то почему не может выводить тип возврата? Почему там лямбды не полиморфны?

Почему я не могу написать let id = |x| x;, а далее использовать это в двух контекстах. Что можно сделать в C++ без проблем.

Правильно, потому что лямбды не являются функциями, а являются фейком. Т.е. на самом деле раст-огрызок не знает ничего о лямбдах - это функции лишь на уровне бекенда.

Он берёт вытаскивает её тело и поставляет в вызов. Далее всё просто. Они так же алиасятся.

Но т.к. оно тупое - оно пытается алиасить лямбду со всеми вызовами(вспоминаем что я выше писал) и в результате получается тех самых два типа внутри одного алиаса.

А где там эти алиасы, какие у них типы - оно знать не знает. Оно может потом по скриптухи пройтись и там что-то в консоль накласть, но не более.

Это примерно тоже самое, что делает линкер в си/цпп. Тоже нихрена не знает ни о чём, ни окаких типах и прочего. И просто может сказать «есть два символа с одним именем».

Вот «вывод типов» в расте такой же тупой как линкер. Там у них битва равна, но хрен знает даже кто победит.

right_security
()
Ответ на: комментарий от right_security

Кстати, доказывается мусорность и неспособность к выводу простым тестов. Если раст выводит типы, то почему не может выводить тип возврата?

Ты поехавший что ли?

Конечно тип возврата выводится

    let id = |x| x;
    let val = id(1);

Как по-твоему раст узнал тип val? Он взял тип 1, по ней узнал тип параметра id, по нему узнал тип выражения (x), по нему узнал тип возвращаемого значения, и уже зная полный тип id смог вывести тип val.

Почему я не могу написать let id = |x| x;, а далее использовать это в двух контекстах.

Чтоб получить правильный ответ необходимо задать правильный вопрос. Правильный вопрос тут скорее такой: а почему ты ждёшь, что оно будет работать? И правильный ответ: потому что ты - идиот.

На твой же вопрос ответить элементарно: в расте происходит вывод типа, а не автоматическое исправление твоих ошибок. Компилятор видит замыкание, компилятор из контекста определяет его тип. Если он встречает противоречащий контекст, он сообщает, что переменная id требуется разного типа, это ошибка.

Как бы они могли «исправить» такое поведение замыканий?

  • добавить функторы с перегрузкой. Но это противоречит идеологии раста, на выкидывание перегрузки
  • эмулировать поведение дженерика, генерировать по одному id для каждого возможного контекста. Но это, во-первых, будет странным, как так если для замыканий создаются копии других типов, почему тогда и для обычных переменных не создать? Плюс к тому это лишит замыкания статуса гражданина первого сорта, их уже невозможно будет передавать в другие функции, например.

Как видишь, всё просто. Главное чтоб в голове мозги были, а не килограмм говна.

Что можно сделать в C++ без проблем.

Так потому что твои плюсы, как ты говоришь, «сраная скриптуха». Внутрь императивного языка со строгой типизацией воткнули декларативный подъязык с утиной типизацией, и теперь обмазываются глюками этого союза и дрочат.

khrundel ★★★★
()
Ответ на: комментарий от khrundel

Если он встречает противоречащий контекст, он сообщает, что переменная id требуется разного типа, это ошибка.

Мне два раза придётся писать один и тот же код лямбды, если у меня два контекста? Ужас. Это косяк языка.

ox55ff ★★★★★
()
Ответ на: комментарий от khrundel

Так почему не пользовались?

Пользовались. И, в низкоуровневом или связанном с сетью коде, пользуются до сих пор.

Зачем надо было колхозить std::variant?

Потому что унаследованный из чистого Си union не позволял помещать в union пользовательские типы с нетривиальными конструкторами/деструкторами.

Если бы при рождении C++ этот недостаток union-а был учтен и в C++ вошел какой-то более продвинутый вариант, надобности в std::variant, возможно, и не было бы.

Я не понимаю сути твоих возражений.

Я не то, чтобы возражаю. Я говорю о том, что ваше высказывание

«Если бы boost::variant был достойной альтернативой std::variant, не было бы необходимости добавлять это в std. Видимо у кого-то есть понимание, что не все подключают буст, а вариант - это такая базовая функциональность, которой не место в левой библиотеке.»

для меня выглядит шизофренично (т.е. содержит ряд противоречий внутри). Т.к. и boost::variant в мире C++ не рассматривают как альтернативу std::variant, и необходимость добавления в stdlib определяется не «достойностью альтернативы», и само добавление базовой функциональности в stdlib в C++ затянулось на десятилетия и из этого не стоит делать далеко идущих выводов.

Для меня писульки Страуструпа не менее важны чем эти же писульки, пропечатанные комитетами.

Ваша точка зрения понятна. Только тут вот какое дело: язык C++ до принятия стандарта существовал в виде «язык C++» лишь пока был всего один cfront. Как только появились другие реализации, то встал вопрос о том, а что же считать C++ и можно ли говорить, что конкретный компилятор поддерживает именно C++. Добавим сюда еще и то, что Страуструп в 1980-е опубликовал, как минимум, две работы, в которых описывался «С++ мечты», не реализованный пока нигде, то вопрос о том, что именно считать C++ становился еще более открытым.

Ответ на этот вопрос был поставлен именно что с появлением C++98.

Видимо Страуструп считал, что нет таких случаев, когда RTTI необходимо, предлагал обходиться нормальным динамическим полиморфизмом.

Так ведь и Страуструп во многом заблуждался. Навскидку можно вспомнить его веру в то, что разработчики компиляторов придумают как хранить стандартную библиотеку в уже скомпилированном и компактном представлении, чтобы #include <iostream> не требовал препроцессинга и повторной компиляции туевой хучи стандартных заголовочников. Не взлетело.

Так же Страуструп добавил в язык очень дорогой механизм обработки исключений (и этот механизм принципиально дорогой по своей идеологии). Что привело к тому, что во многих проектах исключения под запретом.

Посему не нужно придавать словам Страуструпа значение «истины в последней инстанции». Прекрасно видно, что C++ развивается обычным эволюционным путем, методом проб и ошибок. Никакого «хитрого плана», который поэтапно и скрупулезно реализуется с самого начала благодаря железной воле Страуструпа не было.

Не было никакой причины чтоб он не предоставил опционального метода превращения union в tagged union, если бы такое желание у него было.

Мне этого не ведомо. Я сам вряд ли могу вспомнить все причины принятия того или иного решения в собственном проекте 10-летней давности (да даже и в проекте, которому всего год исполнился). А уж брать на себя ответственность о том, что думал и что мог думать совсем другой человек… Даже и не собираюсь.

eao197 ★★★★★
()
Ответ на: комментарий от khrundel

Конечно тип возврата выводится

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

Как по-твоему раст узнал тип val?

Это не функция, это не полиморфный контекст.

Он взял тип 1, по ней узнал тип параметра id

Нет, никакой тип он не брал. Это примитивный алиасинг.

по нему узнал тип выражения (x)

Эта скриптуха не умеет узнавать тип выражения. Показывай мне decltype. Он не может их типы выводить.

по нему узнал тип возвращаемого значения, и уже зная полный тип id смог вывести тип val.

Нет, он ничего не узнал. Ты не знаешь как это работает. А ведь я тебе выше объяснил.

Давая ещё раз объясню для пацанов. Ты, судя по всему, просто фанатик. Понимать что-то тебе ненужно.

Раст не умеет ни в какие лямбды. И ни в какой вывод типов. Когда ты пишешь |x| x - ты на самом деле используешь тот самый _, который просто пустой тип. С ним нельзя ничего делать. Поэтому никакой тип этот мусор не выводит.

let len = |x| x.len();
run(len);

Это не будет работать. В случае с |x| x - это похакано. Так же в некоторых случаях тоже похакано, но важно - никакого вывода типов нет.

Когда ты пишешь этот мусор в контексте run - там _ алиасится на тип аргумента, т.е. в этой скриптухе семантически это не вызов функции. Это просто кусок кода. Не функция.

Поэтому поведение вне контекста вызова/за его пределами отличается.

Как бы они могли «исправить» такое поведение замыканий?

Не, ты запутался в показаниях. Там тебе до перегрузки ещё маняврировать и маняврировать.

эмулировать поведение дженерика, генерировать по одному id для каждого возможного контекста.

Оно итак его эмулирует. Генерики не имеют никакого отношения к типам.

Но это, во-первых, будет странным, как так если для замыканий создаются копии других типов, почему тогда и для обычных переменных не создать?

Эти манёвры. Почему тогда для функций создаётся? А если для функция есть - почему для переменных нет? Как эти жалкие попытки относится к реальности?

Плюс к тому это лишит замыкания статуса гражданина первого сорта, их уже невозможно будет передавать в другие функции, например.

С чего вдруг? Из-за твоих фантазий?

Как видишь, всё просто. Главное чтоб в голове мозги были, а не килограмм говна.

Нет. Твои оправдания не стоят ничего. Просто фанатичные оправдания невежества. Почему это так я объяснил. Мусор не может в вывод типов. Это аксиома.

Поэтому вывода типов для функций нет. А вот если не будет для лямбды - это будет совсем позорище. Поэтому в этой скриптухе лямбда не лямбда, а костыль. Это такой базовой параметрический хак. Он есть везде. Только в менее мусорных языках там не всё так позорно.

right_security
()
Ответ на: комментарий от khrundel

Так потому что твои плюсы, как ты говоришь, «сраная скриптуха». Внутрь императивного языка со строгой типизацией воткнули декларативный подъязык с утиной типизацией, и теперь обмазываются глюками этого союза и дрочат.

Ах да, эти фантазии.

декларативный подъязык

Я уже спрашивал манявратора - что это и где его найти. Он так и не ответил. Повторяешь одну и ту же чушь как бот.

утиной типизацией,

Никакой утиной типизации там нет. Боже, ты бы хоть не позорился так. Утиная типизация - это динамическая типизация(т.е. в рантайма). И вообще это чушь, потому что никакой иной типизации динамической быть не может. Динамическая типизация на то и динамическая, что статически связи неизвестны.

Поэтому ссылка на «утиную типизацию» это просто невежественное название динамической типизации.

В C++ нет динамической типизации. Поэтому сразу мимо.

К тому же, потоки этого невежества завели пропагандиста в тупик, ведь хоть лямбды в расте мусор, но там прибиты хаками элементы фентезийной «утиной» типизации.

Допустим, |x, y| x + y - это так же утиная типизация по методичке. Но теперь нужно оправдываться за то - почему «это другое».

обмазываются глюками

Покажешь глюки? Ты уже сколько фантазируешь и до сих пор не родил глюки.

right_security
()
Ответ на: комментарий от eao197

Потому что унаследованный из чистого Си union не позволял помещать в union пользовательские типы с нетривиальными конструкторами/деструкторами.

Очередной пример того как одни фантазии синергируют с другим. Именно по причине наличия таких «защищаторов» существуют подобные персонажи.

Никакой вариант никакого отношения к union не имеет. Ничего не чинит. И уж тем более union ненужно уметь ни в какие «конструкторы» и прочую невежественную чушь.

Юнион это что? общий сторедж с доступом по именам. Может ли в этом вариант? Нет. Содержит ли юнион тегирование? Нет - оно ему ненужно. Можно ли в принципе сделать вариант на юнионе? Это вообще левая сущность.

Рассказывать какую-то чушь о том, что там нету «деструкторов» - тоже самое, что рассказывать о том, что их нет у указателей/ссылок.

Аналогом union могло быть какое-нибудь common_storage<Ts...>, которая бы брала общий размер/выравнивание и передавала в какой-нибудь std::aligned_storage.

Но всё равно основной фичи нет - доступа по именам.

Вариант нужен и создавался для иного. Уж тем более его часть связанная с visit вообще вне юниона и растомусора. Никакого отношения не имеет к ним.

Проверяется эта чушь очень просто. Мы берём кейс без фантазии про конструкторы и бам - никакой visit не работает, никакая сериализация/десериализация полиморфизма не работает.

Если бы вариант расширял/решал те проблемы, о которых фантазирует данный персонаж - всё бы работало. А так не работает.

right_security
()
Ответ на: комментарий от right_security

И уж тем более union ненужно уметь ни в какие «конструкторы» и прочую невежественную чушь.

Комитет ничего не знал про мнение царя и добавил в С++11 unrestricted unions. Вероятно именно потому, что union не нужно ни во что уметь.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Комитет ничего не знал про мнение царя и добавил в С++11 unrestricted unions.

И ты тут же умножил свои попытки на ноль, потому как если эти доводы были в ползу вариант, то они отменяются. Бывает, когда запутываешься в показаниях. Вроде нёс одну чушь, потом нагуглил/«вспомнил» другую - решил срывать покровы, но забыл о предыдущих показаниях

Вероятно именно потому, что union не нужно ни во что уметь.

Юниону и ненужно ни во что уметь. Просто ранее накостыляли ограничение, которое выпилили. Таким образом попытки пастить мне что-то лишь привели к очередным противоречиям в показаниях.

Если непонятно я поясню. юнион не должен мочь. Но почему-то кто-то посчитал, что он мочь должен. Но осилить ничего не осилил. Поэтому просто похакали. В целом как много чего в си с классами.

А далее как раз таки это отменили, потому как юнион не должен мочь ни в какие конструкторы. Но чтобы совсем не позорится + для удобно - обмазали юнион сахарком. Который позволил накласть на него методы без обмазывания структурой.

Но это ничего не поменяло - юнион как не мог так и не может. И никто даже не пытался заставить его это делать.

right_security
()
Ответ на: комментарий от khrundel

Давай посмотрим правде в глаза: ты написал глупость про цепочку if. Которая, видимо, в плюсах тебя достала.

Отличные проекции. К твоему сведению: я даже не помню, когда я последний раз писал больше одного if x is Y подряд.

Скорее всего думал, что там реально что-то типа RTTI используется. Потом ты узнал, что в Расте всё не так. Но признать этого не мог, вот и начал вертеться, типа да, непохоже, зато «в душе» (т.е. семантически, ага) оно та же цепочка ифов.

Боже, ты так ничего и не понял.

При чём тут Раст? Кто-то говорил, что в Расте матч изобрели что ли?

Бзззт, тут ошибка. Никто не объяснил почему матч говно.

Тебе два человека пытаются объяснить, и уже не по одному разу.

Ты чего-то ныл про то что у тебя он по какой-то непонятной причине вызывает ассоциации с лапшой из if (x is A), но ни объяснить внятно, откуда эта ассоциация взялась, ни почему это плохо ты так и не смог.

Четвертая, если не ошибаюсь, попытка. match семантически эквивалентен цепочке if, потому что проверяет значение на принадлежность закрытому множеству типов. Это множество невозможно расширить, не изменяя исходного кода в каждом из мест, где происходят проверки.

Я в очередной раз подчеркну, что мне глубочайше наплевать, как конкретно выполняется проверка: RTTI это, if или switch по typeid, индексу или вообще libastral.

Я же тебе вполне внятно объяснил, почему шаблон в матче, да ещё и с утиной типизацией - это лютый п***ц. Возражений не услышал, ну кроме «а в плюсах всё такое же кривое».

Ты написал про какие-то мины, которые кому-то закладываются и обязательно выстрелят. Я написал, что так делается всю жизнь, и еще ни разу за десятилетия не выстрелило. Ты мне ничего не ответил и сейчас ушел в отрицание – очень глупо, вся история сообщений перед глазами. Rust: преобразования указателя на трейт в конкретный тип (комментарий)

Ты настолько обосрался, что стал взывать к авторитету окружающих?

…весь комментарий @eao197 ты свел к «Нет, я так не считаю». Ты мне говоришь, что я обосрался?

Нет, он не ответил, он даже не понял о чём я собственно писал.

Это ты не понял, о чем он ответил.

Моя вина, видимо, но теперь надеюсь понятно объяснил? Если кратко: буст не в счёт.

Ты написал полный бред. Языку нельзя ставить в упрек наличие или отсутствие в std какой-то фичи. Это можно ставить в упрек его std. Можно вообще не иметь Boost.Variant и std::variant – и написать свой, полностью функциональный и рабочий аналог. Удачи «написать» свой аналог match.

Чего сложного в твоём коде?

потому что ресолвить разные типы идентификаторов одним и тем же способом явно нельзя

Я повторю вопрос, ты точно понял, что там написано? Где там используется «один и тот же способ» для разных типов идентификаторов?

Но я даже пойду у тебя на поводу, и напишу

void resolve(const Identifier& id) {
    std::visit(overloaded {
        [](Id id) { resolve_index(id.value); },
        [](const Name& name) { resolve_name(name.name); },
        [](const Uuid& uuid) { resolve_uuid(uuid); },
        [](auto && id) { resolve(id); }
    }, id);
}

Что-то кардинально поменялось?

Ты писал именно что они плохи.

Да, потому что они неполиморфны.

Стоп. А не посрать? Повторяю, ты со своим полиморфизьмом носишься как дурень со ступкой. Всем насрать на твой полиморфизм. Если что-то полиморфно - это не значит хорошо.

Нет. Ты реально поломался. Если бы тебе не нужен был полиморфизм, ты писал бы на сишке и паскале, а на расте не трогал бы трейты. Если он тебе нужен, то ты им пользуешься, а не говоришь «ну мне достаточно, больше не надо».

Пока что я вижу как ты верещишь и с перепугу даже призвал в свидетели Царя.

Роман сам пришел, я его не призывал – и пока что ни одного реального ответа кроме хамства ты ему не написал. Если ты что-то из его сообщений опровергнешь, тогда пиши мне про «призыв в свидетели».

Давай ещё раз повторю: единственное достоинство, которое ты заявил - это то, что в случае добавления нового варианта компилятор не предупредит тебя о тех местах, где не хватает обработки данного варианта.

Ты лжешь. Возможно, намеренно. Более того, ты опять показал свою некомпетентность. Не просто так я в третий раз задаю вопрос, точно ли ты понял код выше.

Я писал в сообщении Rust: преобразования указателя на трейт в конкретный тип (комментарий)

Замени я const Identifier на любой другой variant-like тип – код продолжит работать. Добавь я auto&& обработчик – код продолжит работать, и даже лучше, чем сейчас. Потому что это полиморфный код.

Как это соотносится с тем, что ты «цитируешь» выше? Никак. Более того, в случае добавления нового варианта без обработчика компилятор предупредит, что для него нет обработчика.

Ты сам же писал, что цепочка ифов, равно как и матч, предназначены для перебора закрытого набора вариантов.

В этом же сообщении чуть ранее он притворяется, что не понимает этого. Типично.

При этом матч предупредит тебя, если ты чего забыл

Нет, если в нем есть _ => { ... }. Впрочем, это не важно, я уже в который раз подчеркиваю, что сравниваю их с точки зрения проверки закрытого множества типов и расширяемости этого множества.

Плюсы, если что, просрали все полимеры, в линупс-ведро их не пустили.

Да, Rust вот пустили, из-за толпы фанбоев. Ладно, оставлю Роме этот диалог. Ну как пустили, писать обертки для драйверов. Ну как обертки – позорище, если код почитать.

Из прикладухи их почти вытеснили, живут за счёт легаси, которое слишком дорого переписывать.

Да ладно? Мужики и не знали.

Раст, являющийся в чём-то более низкоуровневым чем плюсы,

Раст это язык для виртуальной машины. Он не может быть низкоуровневым. Задумывался когда-нибудь, что значат VM в LLVM? Задумывался, почему у раста нет своего компилятора?

набирает популярность, а плюсы стагнируют.

Мужики и не знали (x2). Почитай papers, что ли.

которые понимают что ООП - говно, общий интерфейс, может пригодиться.

Но речь не об этих случаях.

Конечно, только это ты начал речь про классическое ООП, потому что тебе не давало покоя то, как это делали в древних плюсах. Кушайте, не обляпайтесь.

Ты только что признал ущербность match в пользу трейтов. Осталось немножко подрасти и понять, что трейты это в свою очередь кастрированная версия нормальной перегрузки.

Так что не надо с поля, на котором ты проиграл, пытаться незаметно переползти на другое.

Еще раз, это ты с самого начала пытался свести тему к С с классами. Особенно это забавно на фоне непонимания крестового кода.

Можешь дать приблизительную оценку, насколько редко используют енумы в расте? А я посмеюсь.

Даю «приблизительную» оценку: ни одна библиотека из most downloaded на первой странице crates.io не использует в публичном интерфейсе enum ни для чего, кроме передачи флагов.

Оно позволяет N сократить до минимального необходимого.

Нет, там не существует «минимально необходимого». Для N кейсов там будет N веток. В лучшем случае ты напишешь _ => panic! и потеряешь варнинги.

необходимость писать объявление и реализацию

Нет такой необходимости.

А за неё ещё GoF советовали по рукам бить.

Это никого не волнует, кроме любителей ссылаться на чужой авторитет и подменять предмет спора с полиморфного auto на С с классами и наследования.

Siborgium ★★★★★
()
Ответ на: комментарий от khrundel

Может потому что люди предпочитают подумать перед тем как делать?

Вполне возможно. Мне лично наплевать, думают они или нет – получается одинаково плохо.

В смысле тупым? А что он должен был сделать умного?

Да. Ты мог бы попытаться задвинуть про соотношение сил и результата, но не может считаться приемлемым бч, неспособный одновременно борровить два разных элемента слайса.

Так и у плюсов нет своего компилятора, тот же убогий фронт к LLVM и GCC.

Во-первых, LLVM это компилятор, написанный для крестов и на крестах. GCC не целиком, но на большую часть для крестов, и на значительную – на крестах. Во-вторых, фронт фронту рознь, и это становится понятно, если сравнить количество работы, проделываемой clang и rustc. При на порядки более высокой сложности С++ порождаемый clang код вполне достойно выглядит, даже если не сравнивать его с гигабайтами мусора на выходе rustc.

где никакой двусмысленности на тему «что вызывается» со стороны компилятора нет, один только человек, читающий код, вынужден тратить усилия на ресолв.

Нет, двусмысленность есть, так как известен лишь трейт, но никак не реальный тип. Если бы это был реальный компилятор, он мог бы вывести, какой тип подается на вход этой функции в данном контексте и предложить его, но извини, у тебя есть только Rust.

это буквально vtable

Бугагашеньки. Какая нахрен втейбл? Нет у компилятора втейбла

Ты идиот?

Ему нужно посмотреть что это за тип, а потом выбрать функцию с этим именем у которой селф соответствующего типа. Просто? Да, просто. А теперь берём твой вариант: взять переменную, узнать её тип, взять список реализованных трейтов, найти среди них тот, в котором есть функция с таким именем, если найден узнать индекс во втейбл и взять оттуда, если не найден, взять простые методы. Тоже в общем-то просто, но уже сложнее.

Оба метода показывают в корне непонимание модели типов в Rust. Семантически происходит следующее: ты определяешь список трейтов, реализуемых идентификатором, после чего имя функции остается найти среди определяемых этими трейтами. Реальный тип здесь нигде не фигурирует, его нет, и поэтому нет «простых» методов. Именно потому что типа там нет, а есть только табличка методов и трейтов, к которым они принадлежат, которую ты определяешь руками, поэтому речь и идет о «буквально vtable».

«что все вокруг идиоты и не могут решить простую задачу.»

демонстрирует поразительные уровни синдрома Даннинга-Крюгера

Во-первых, ты некорректно описал поиск допустимых цепочек преобразований. Во-вторых, ты забыл про ADL. В-третьих, ты забыл про SFINAE. На этом можно остановиться, ты уже описал не С++, твоя аналогия – дерьмо, С++ ты не знаешь, что уже неоднократно показал.

Да-да-да, когда изобретут новый тип шейпа, тогда и приходи.

Без проблем. Раз ты разделяешь Line и Point на два типа, то я могу спокойно выделить пачку различных сплайнов, кривых Безье и не только, геометрические фигуры и т.д., просто следуя твоей логике разбиения.

Про дальнейшее Роман уже подробно тебе отписал. Никакой сложности вывод типов в Rust не представляет, это полностью линейный процесс, исключение составляют только лямбды, для которых специально сделан хак, столь же примитивный впрочем.

Siborgium ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.