LINUX.ORG.RU

Rust 1.49

 


2

6

Опубликован релиз 1.49 языка программирования Rust.

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

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

  • Уровень 3. Система поддерживается компилятором, но не предоставляются готовые сборки компилятора и не прогоняются тесты.

  • Уровень 2. Предоставляются готовые сборки компилятора, но не прогоняются тесты

  • Уровень 1. Предоставляются готовые сборки компилятора и проходят все тесты.

Список платформ и уровней поддержки: https://doc.rust-lang.org/stable/rustc/platform-support.html

Новое в релизе 1.49

  • Поддержка 64-bit ARM Linux переведена на уровень 1 (первая система, отличная от систем на x86, получившая поддержку уровня 1)

  • Поддержка 64-bit ARM macOS переведена на уровень 2.

  • Поддержка 64-bit ARM Windows переведена на уровень 2.

  • Добавлена поддержка MIPS32r2 на уровне 3. (используется для микроконтроллеров PIC32)

  • Встроенный тестовый фреймворк теперь выводит консольный вывод, сделанный в другом потоке.

  • Перенесены из Nightly в Stable три функции стандартной библиотеки:

  • Две функции теперь помечены const (доступны на этапе компиляции):

  • Повышены требования к минимальной версии LLVM, теперь это LLVM9 (было LLVM8)

>>> Подробности

★★★★

Проверено: Shaman007 ()

Ответ на: комментарий от red75prim

А разработчики собираются сделать эти функции константными. Будет compile-time runtime type information. Хех.

Конечно, возьмут фичу из LLVM9, которую делали для плюсов и переиспользуют. Всё правильно :)

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1327r1.html

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

возьмут фичу из LLVM9

Это constexpr для виртуальных методов и полиморфных по наследованию типов. TypeId::of::<T>() - не виртуальный метод и иерархии типов по наследованию в расте нет. Так что нет, не то.

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

Появляется понятие оверлоад сета и ранжирование специализаций.

Или: часть запутанных сложных правил разрешения перегрузок C++ можно компактно выразить в системе типов с ограниченным параметрическим полиморфизмом и специализацией.

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

Будет compile-time runtime type information. Хех.

Это должно было быть шуткой? Получилось не очень, потому что, внезапно, RTTI действительно можно вычислять в компайл-тайме для известных в статике типов.

Используется самая «специализированная» специализация.

Сейчас в найтли можно сделать так

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

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

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

которая вносит концепт перегрузки в раст

Концепт перегрузки итак давно есть. без этого вообще ничего полезного сделать было бы нельзя. Просто сейчас он довольно примитивный. Можно опрделить трейт для i32, для u8, и скажем для Veс<T>, но вот определить поведение по умолчанию нельзя т.к. при конфликте перегрузок сейчас компилятор сразу посылает нафиг.

все еще сковывает информацию о типах одним интерфейсом

Ну сковывает несколькими интерфейсами.

Это не поможет в реализации static if

Если речь идёт о поднятии constexpr bool на уровень типов то тут облом в расте, и вряд ли добавят когда то.

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

Либо сделают, сломав имеющуюся модель, как это было с async-ами.

И что?

В С++ я мог бы написать

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

Невозможность вызвать произвольную функцию для T - это вполне себе (полезная) фича.

В случае ошибки конструктор выбрасывает исключение.

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

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

И что?

А вы считаете это чем-то хорошим? Тезис: в С++ сложно, можно проще, мы сможем проще. Получилось в итоге сложнее, про синтаксис я вообще молчу (при том, что синтаксис раста мне в целом нравится), а модель так хороша, что в основе раста – трейтах – нельзя требовать асинхронных функций, и приходится этого добиваться через макропонос.

что в каком-то странном случае мне кровь из носу надо создавать объект без исключений

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

это вполне себе (полезная) фича.

Где польза?

но пока кажется, что ты хочешь писать один в один как на плюсах, но не получается.

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

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

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

В их отсутствие упирается вообще все. Но даже если бы они появились, затирание типов не позволит писать ничего полезного.

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

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

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

В их отсутствие упирается вообще все. Но даже если бы они появились, затирание типов не позволит писать ничего полезного.

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

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

а с конкретной фичей, которую еще не доделали.

и таких фич около десятка, и чтобы их все сделать надо практически переписать всё с нуля =)

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

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

Бида-бида. Ну тогда осталось дождаться модулей, async, ADT, safe/unsafe, expected, паттерн матчинга, стабилизации C++20, нормальных строк, менеджера пакетов, альтернативы cmake, объявить сишный мусор, типа неявного приведения, устаревшим в плюсах, и Rust не нужен.

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

Трёхэтажные шаблоны ненужны даже в плюсах.

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

Ну т.е. я в курсе про рейтрейсинг на шаблонах,

Никому не нужен рейтрейсинг на шаблонах. Зачем кому-то писать рейтрейсинг один раз на шаблонах, второй раз для рантайма?

А что именно полезного? У меня на работе вообще нигде нет С++ кода, который задействует что-либо из примеров на последних трёх страницах.

Ну значит у вас на работе пишут си с классами разлива в лучшем случае С++03.

что их нельзя заменить на небольшой пердолинг с макросами?

Темплейты никак не заменяются макросами, тем более этим убогим препроцессором.

но насколько часто такие вещи реально требуются

Кому требуются? Я хочу писать хороший, выразительный, полиморфный код. Мне эти вещи необходимы. Это не вопрос языка – я бы его охотно сменил, но ничего подобного нигде нет.

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

Ну тогда осталось дождаться …

Могу только привести цитату.

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

А так – крайне спорный список фич.


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

Трёхэтажные шаблоны ненужны даже в плюсах.

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

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

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

Define хороший, define выразительный, define полиморфный. Пока это всё слишком абстрактно.

Скажем, понятно чем плох тот же самый push_back: делаем лишнее копирование. Делаем лишнее копирование из-за того, что может понадобиться реаллокация, которая может зафейлить. Мы не можем поменять местами реаллокацию и конструктор, потому что в присутствии исключений, деструкторов и кастомных аллокаторов это было бы неэквивалентной трансформацией. В плюсах еще не было перемещения, но это относительно тривиально. Есть два решения: либо разделить push_back на аллокацию и инициализацию, либо сделать вторую функцию, которая сначала выделяет память, а уже после этого вызывает конструктор. Для того, чтобы вызов конструктора можно было вкорячить в середину функции из стандартной либы, мы можем использовать либо вариадики, либо лямбды.

Вот: конкретная проблема, конкретный виновник, конкретное решение. Решение не 100%, потому что нафигачить неоптимизируемой херни тебе никто не запретит, но это будут проблемы в коде, который полностью под твоим контролем, значит ты можешь его исправить не форкая std на каждый свой класс.

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

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

делаем лишнее копирование

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

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

В плюсах еще не было перемещения, но это относительно тривиально

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

emplace от push_back отличается вообще не копированием, копирование никакого значения не имеет. push_back – это конструкция объекта из объекта, передаваемого аргументом. Это может быть move, это может быть копирование – неважно, ключ в том, что объект создается из объекта. emplace конструирует объект на месте, и не из другого объекта, а из произвольных аргументов, которые emplace просто передает в конструктор. Ты не можешь написать emplace без форвардинга и вариадиков – это просто невозможно сделать в общем случае. Можно захардкодить какой-то конкретный, но это бесполезно, это ничем не лучше сишки.

что код, который не генерирует ни одной инструкции в конечный бинарь, остаётся динамическим

Речь не о инструкциях, а о семантике.

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

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

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

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

Я не знаю. Мне shadowing не нравится. Наверное, это было очень нужно в Ocaml, так как там нет ключевого слова mut. А в Rust и F# это перешло просто так, как некоторые фичи из C в C++…

Нет, mut о другом. Shadowing выгодно смотрится когда есть код типа такого:

let string_id: String = get_id(...);
let parsed_id: u8 = string_id.parse();
let validated_id = validate(parsed_id);

Пример синтетический, но думаю, что идея понятна.

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

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

и еще, если хочешь пожаловаться на синтаксисы, то посмотри на синтаксис раста https://doc.rust-lang.org/stable/rust-by-example/conversion/try_from_try_into… и скажи что тебе понятнее, си или вот это вот что по ссылке.

И что там по ссылке такого страшного? Даже лайфтаймов нет.

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

Примеры кода из темы про zip-итератор до сих пор снятся мне в кошмарах. А теперь смотрим на Rust версию - обычный, человеко-читаемый код.

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

Я и примеры кода из темы про zip-итератор считаю человекочитаемыми. Если (когда?) в расте будут вариадики, пример будет выглядеть точно так же.

Сравнение, кстати, некорректно – zip-итератор решает задачу связывания произвольного числа итераторов, а Rust версия только двух. Наконец, большая часть кода в той теме написана на заскорузлых плюсах 11 года.

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

Я и примеры кода из темы про zip-итератор считаю человекочитаемыми

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

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

Ну наприседайте на zip от произвольного числа аргументов в Расте. Хотя бы на 2..32. Сравним результаты.

Приседания в теме связаны как раз с древностью плюсов, используемых в ней. Я считаю, что версия царя отлично читается.

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

Я и примеры кода из темы про zip-итератор считаю человекочитаемыми.

А где гарантия что лямбды заинлайнятся? вот у меня один раз код царя на одном из компиляторов не заинлайнился. Можно ли в лямбду добавить gnu::always_inline?

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

Ну наприседайте на zip от произвольного числа аргументов в Расте.

А смысл? Кому может понадобиться zip от 32 списков? Правильно - никому.

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

При чем тут гарантии на инлайн лямбд? Как они связаны с zip-итератором?

вот у меня один раз код царя на одном из компиляторов не заинлайнился.

На каком из компиляторов, какой код?

Можно ли в лямбду добавить gnu::always_inline?

Да.

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

Без вариадиков – никому. С вариадиками несложно увидеть юзкейс и для большего числа.

Хотя бы на 2..5 напишите, раз уж вас здравый смысл волнует.

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

а Rust версия только двух

И, сюрприз, один из этих итераторов (или оба) может быть zip-ом.

zip многих итераторов делается макросом. Наверно можно написать и на HList’ах из frunk’а, но это type-level ради type-level’а.

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

Это не сюрприз, это очевидно.

zip многих итераторов делается макросом.

…Как и все остальное, на что раст не способен сам. Вопрос про […]32 был задан специально – именно до такой глубины в стандартной библиотеке раста для массивов реализуются трейты. Для туплов ограничиваются всего 1..12 элементами. И если для массивов хватит const generics, то для туплов будут необходимы вариадики.

До тех пор все это придется реализовывать через копипасту и макросы – негативное влияние копипасты на компиляцию оценили авторы ArrayVec, где уже на 128 элементах наблюдаются значительные тормоза.

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

…Как и все остальное, на что раст не способен сам.

Ну это примерно как «С++ использует темплейты для всего на что не способен сам».

то для туплов будут необходимы вариадики.

Не необходимы. Упростят написание - да, но гетерогенные списки произвольной длины можно делать и на HList’ах.

именно до такой глубины в стандартной библиотеке раста для массивов реализуются трейты

Устаревшая информация. Сейчас это относится только к Default. Остальные перевели на const generics.

на 128 элементах наблюдаются значительные тормоза

Да, есть такое. const generic’ами занимаются.

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

Ну это примерно как «С++ использует темплейты для всего на что не способен сам».

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

Устаревшая информация. Сейчас это относится только к Default. Остальные перевели на const generics.

Хорошо, я не знал этого. Когда я последний раз интересовался этой темой, этот переход только готовился.

Упростят написание - да, но гетерогенные списки произвольной длины можно делать и на HList’ах.

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

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

Я хотел бы продолжить разговор о том что такое «базовый язык» и почему его выделяют отдельно от раста. Почему бы не пойти дальше и не разбить «базовый язык» на язык выражений (которые в блоках) и язык объявлений типов и функций? Там же тоже два языка.

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

раст лучше не становится.

Ну, блин, уже не знаю как и объяснить.

Template metaprogramming в С++ может быть описано как нетипизированное лямбда-исчисление. Дженерики в расте - это типизированное лямда-исчисление. Оно не хуже, оно другое.

Напишите zip на frunk, чтобы нам было о чем говорить.

Можно и без франка написать (рекурсия на уровне типов по туплу (Head1, Tail1), где Tail1 - это (Head2, Tail2) или () и т.д. ) Но сначала скажите, чем (a).zip(b).zip(c) и т.д., не нравится? Тем, что там ).zip( вместо запятой?

Потому что zip с рекурсией по туплам будет выглядеть так

Zip::zip((a, (b, (c, ()))));

Как я уже говорил поддержка на уровне типов операции (A, B, C, …) -> (A, (B, C, …)) на уровне языка конечно не помешала бы.

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

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

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

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

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

Template metaprogramming в С++ может быть описано как нетипизированное лямбда-исчисление. Дженерики в расте - это типизированное лямда-исчисление. Оно не хуже, оно другое.

Зачем его описывать? Все языки можно описать как машины Тьюринга, и сделать вывод об их эквивалентности, но это не имеет никакого смысла. Кому нужно лямбда-исчисление? Где avx-инструкции?

Можно и без франка написать

Ну напишите.

Потому что zip с рекурсией по туплам будет выглядеть так

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

поддержка на уровне типов операции

Не будет, потому что сломается вызов функции от туплов.

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

Ну напишите.

Сначала ответьте на вопрос ниже.

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

Расскажите подробно, каким образом полиморфная функция Zip::zip(self), принимающая типы вида (T1, ()), (T1, (T2, ())), (T1, (T2, (T3, ()))), - это «мономорфная функция от двух переменных».

Она никак не может быть мономорфной (так как принимает на вход разные типы). У ней один аргумент: self.

Вот её реализация, на всякий случай.

trait Zip {
    type Out;
    fn zip(self) -> Self::Out;
}

impl<T, U: Zip> Zip for (T, U) {
    type Out = Iter<(T, U::Out)>;
    fn zip(self) -> Self::Out {
        let (t, u) = self;
        Iter((t, U::zip(u)))
    }
}

impl Zip for () {
    type Out = Iter<()>;
    fn zip(self) -> Self::Out {
        Iter(())
    }
}

Если бы была поддержка деконструирования туплов, то 6-я строка выглядела бы как

impl<T, U: Zip> Zip for (T, U...) {

А вызов: Zip::zip((a, b, c))

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

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

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

void foo(int i) {
  std::cout << "no template\n";
}

template <typename T>
void foo(T i) {
  std::cout << "template\n";
}

по стандарту, при вызове foo с интом выполнится первый вариант, с любым другим типом - второй.

В то же время растовые макросы - это просто препроцессор, который с точно таким же успехом можно написать хоть на питоне.

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

не может быть мономорфной

impl<T, U: Zip> Zip for (T, U) {

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

struct Zippable {
    void (*zip)(void*, void*);
    void* data;
};

struct Zippable zip(void*, struct Zippable*) { /* ... */ }

или на джавовский Object.

принимающая типы

Она принимает тип T и U: Zip, больше никаких типов она не принимает. Все, что вы перечислили – инстансы этих типов. Вы написали функцию от двух аргументов. Я прошу: сделайте мне zip произвольного числа типов и аргументов. Не двух типов, рекурсивно построенных, а произвольного их числа.

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

Ты можешь без потери смысла переписать код на

Ну и? Вариадики можно переписать как линкед-листы и делать всё в рантайме. Они тоже мономорфные?

То, что сейчас нельзя поставить const перед функцией zip, и сделать её гарантированно compile time - это не принципиальное ограничение, а ещё не стабилизированная фича.

Напишите variadic templates на C++-98 (10 лет с выхода), чтобы сравнивать яблоки с яблоками.

Она принимает тип T и U: Zip,

       Один параметр. Одного типа.
       vvvv
fn zip(self) -> ...
       ^^^^
       С этой стороны тоже один.

Не двух типов, рекурсивно построенных, а произвольного их числа.

Без потери смысла я могу переписать функцию принимающую (T1, T2, T3, …) в функцию принимающую (T1, (T2, (T3, … ))).

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

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

Вариадики можно переписать как линкед-листы и делать всё в рантайме. Они тоже мономорфные?

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

Один параметр. Одного типа.

impl<T, U: Zip> Zip for (T, U) {

Ваш трейт параметризован T и U: Zip. Фактически там два параметра: аргумент и следующий zip.

Без потери смысла я могу переписать функцию принимающую (T1, T2, T3, …) в функцию принимающую (T1, (T2, (T3, … ))).

Ну вот не можете. (a, b, c) <> ((a, b), c).

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

Я вам говорю про семантику того, что вы пишете.

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

Ваш трейт параметризован T и U: Zip.

Трейт ничем не параметризован. Он реализован для множества типов вида (T1, (T2, … (TN, ()) … )).

Ну вот не можете. (a, b, c) <> ((a, b), c)

С чего бы это? Между этими формами записи есть взаимно однозначное соответствие. Конверсия между ними тривиальна, так что семантически (хех) это - одно и то же.

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

Скобки не так расставили. Любая функция вида a->b->c может быть записана как

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

Трейт ничем не параметризован. Он реализован для множества типов вида (T1, (T2, … (TN, ()) … )).

Ваша правда, я некорректно выразился. Я имел в виду реализацию, конечно.

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

Вы адекватный человек?

  1. Там нет никакого оверлоадинга. Вообще.

  2. Он не может быть динамическим семантически, потому что там нигде не затирается тип. Ваш же код с семантической точки зрения эквивалентен (T, impl Zip), а никакому не вариадику.

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

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

С чего бы это? Между этими формами записи есть взаимно однозначное соответствие.

Нет, взаимно однозначного нет. Доказывается тривиально:

((a, b), c) -> (a, b, c)
(a, (b, c)) -> (a, b, c)

Конверсия между ними тривиальна, так что семантически (хех) это - одно и то же.

Нет конечно, хотя бы потому что функция от (a, b, c) и функция от a, b, c не равнозначны. В хаскеле и подобных – да, здесь – нет.

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

практически коэрсия туплов в один тупл хуже ее отсутствия.

Это не коэрсия. Это разные способы представления гетерогенного списка произвольной длины.

Там нет никакого оверлоадинга. Вообще.

Да, встроенный в язык итератор по списку типов, позволяет обойтись без использования type-level рекурсии, с остановкой рекурсии с помощью оверлоадинга. Но принципиально это ничего не меняет.

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

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

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

Мне это тоже начинает надоедать.

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

Ваш же код с семантической точки зрения эквивалентен (T, impl Zip), а никакому не вариадику.

А ваш вариадик семантически эквивалентен List<TypeTaggedVar>. Почему нет?

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

Нет, взаимно однозначного нет. Доказывается тривиально:

Повторяю. Множество типов вида (Head1, Tail1), где Tail1 - это (Head2, Tail2) или (), и т.д. имеет взаимно-однозначное соответствие с множеством типов вида (Head1, Head2, …)

Попробуйте ещё раз.

В хаскеле и подобных – да, здесь – нет.

Где здесь? В рас++те? Кто запрещает определить ABI для fn(A, B, C), fn((A, B, C)), и fn((A, (B, (C, ())))) как идентичные?

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

Да, встроенный в язык итератор по списку типов

Нет никакого итератора.

А ваш вариадик семантически эквивалентен List. Почему нет?

Потому что не эквивалентен. List<TypeTaggedVar> даже в компайлтайме с т.з. языка – тип, параметризованный одним типом. Положим, я буду из него вытаскивать значения как-то. Как вы мне предлагаете а) добывать их тип из тега и б) передавать их в функцию? Если первую проблему можно как-то решить в С++, Zig и Idris, возможно в Haskell с расширениями, то вторую – нигде, кроме как в С++, и (внезапно) используя именно variadic-выражения. Т.е. для эквивалентности A и B нам нужно B отображать в A, используя A, из чего следует неэквивалентность.

Но принципиально это ничего не меняет.

Меняет – например, вы не можете сделать f((T1, (T2, (T3, ())))), для f(T1, T2, T3).

разницу между стиранием типов и параметрическим полиморфизмом

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

Типы там в рантайме не стираются, функции все полиморфные

Нет, в динамике функции как раз мономорфные.

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

Тип вида […] имеет взаимно-однозначное соответствие

А почему вы для других видов не предлагаете вводить такое соответствие? Почему я должен писать

f: a, ((b, c), d) -> e    но     f: a, (b, c, d) -> e

Ваша идея, кстати, плоха еще на этапе (опять же) затирания информации об исходном типа и приведения к какому-то базовому деноминатору.

Где здесь? В рас++те? Кто запрещает определить ABI

Вперед, определяйте ABI. Что вы несете, какой ABI (еще и в расте), какие-то фантазии про какие-то магические туплы, и все это ради того, чтобы не пришлось сказать: пока что нету вариадиков. Цирк.

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

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

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

Нет никакого итератора.

template <typename ...T>
std::list<std::tuple<T...>> simple_zip(std::list<T>... lst)
{
  std::list<std::tuple<T...>>  result;
  for (int i = 0, e = std::min({lst.size()...}); i != e; i++) {
    result.emplace_back(std::move(lst.front())...);
                           А это что такое?   ^^^
    [](...){} ((lst.pop_front(), 0)...);
                          Или это? ^^^
  }
  return result;
}

Ну и что здесь нельзя скомпилировать так, чтобы отмеченные итерации по типам происходили в рантайме? Компилировать вызов такой функции, естественно, надо с преобразованием списка параметров в список TypeTaggedVar. В качестве тэга можно использовать typeid(T) из RTTI.

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

trait Foo {
    type Output;
}

impl<T> Foo for T {
    type Output = Vec<T>;
}

fn main() {
    let vs: <u32 as Foo>::Output = vec![];
}

Взяли тип u32, вычислили над ним функцию Foo, и получили тип Vec.

Впрочем, судя по предыдущим случаям непонимания, вы наверно имели в виду: «Взять значение, принадлежащее неограниченному множеству типов, и выполнить над ним действие, определённое только для некоторого множества типов»? Надеюсь такая формулировка ясно показывает, что это невозможно?

Нет, в динамике функции как раз мономорфные.

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

def foo(a):
  if type(a) is int:
    #do int processing
  elif type(a) is str:
    # do str processing
red75prim ★★ ()
Ответ на: комментарий от vlad9486

Шаблоны в С++ типизированы. Что же всё так плохо. Никакого typename в говнорасте нет. Там нету полиморфизма, что же у вас так плохо с методичками, повторю.

Вот функция в говнорасте, генерик:

fn f<T>(x: T) -> T {
    x
}

Вот во что она компилируется:

fn f(_1: T) -> T {
    debug x => _1;                       // in scope 0 at src/lib.rs:3:9: 3:10
    let mut _0: T;                       // return place in scope 0 at src/lib.rs:3:18: 3:19

    bb0: {
        _0 = move _1;                    // scope 0 at src/lib.rs:4:5: 4:6
        return;                          // scope 0 at src/lib.rs:5:2: 5:2
    }
}

Ты здесь видишь полиморфизм? Нет, потому как его нет. Далее вот это f преобразуется в шаблон, в котором уже есть typename и инстанцируется.

Никаких иных возможностей реализовать статический полиморфизм не существует. Только template. Это аксиома. Генерик-говно в говнорасте как было мономорфным, так и осталось мономорфным после компиляции.

По поводу типизации. Типизация нужна потому, что по-сути f<T: X>(x: T) - эквивалентно, полностью, f(x: X). И чтобы вот у этого x был какой-то тип, у которого можно получать свойства - там и нужна типизация, дополнительная. Никаких других причин нет.

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

Потому в расте нужны еще и макросы, а в пюсах не нужны.

Не поэтому.

beydoderke ()
Ограничение на отправку комментариев: только для зарегистрированных пользователей