Обобщённые типы
Список\<T\>
Список из целых числа = Список из целых();
числа.добавить(10);
числа.добавить(20);
целое первое = числа.получить(0);
целое количество = числа.размер();
числа.удалить(0); // удаляет элемент по индексу, сдвигая хвост влево
числа.освободить(); // как и Словарь — не освобождается автоматически, нужно явно
удалить(индекс) корректно уменьшает размер() на 1 и, если элементы —
структуры, освобождает (ARC) удаляемый элемент.
⚠️ Сам Список из T (как и Словарь) не освобождается автоматически при
выходе из области видимости — вызывай .освободить() явно, если список
больше не нужен раньше конца программы. Для элементов-структур
.освободить() дополнительно корректно отпускает (ARC-release) счётчик
ссылок каждого элемента — если на элемент нет других ссылок (например,
через ранее сохранённый .получить(i)), он тоже освободится; если есть
— останется жить у той, другой ссылки. Для элементов-текста
(Список из текстов) отдельные элементы НЕ освобождаются (та же
двусмысленность литерал/malloc, что и у Словарь.освободить(),
см. ниже) — освобождается только сам список-контейнер.
Словарь\<Ключ, Значение\>
Словарь из текста и целого возрасты = Словарь из текста и целого();
возрасты.положить("Аня", 25);
результат из целого возраст = возрасты.получить("Аня"); // ключ может отсутствовать!
если (возраст.успешно)
вывести(целое_в_текст(возраст.значение));
конец
целое количество = возрасты.размер();
возрасты.освободить(); // Словарь не участвует в ARC — освобождать явно
⚠️ В отличие от Список.получить(индекс) (возвращает значение
напрямую), Словарь.получить(ключ) возвращает результат из Значение —
ключа может не быть в словаре, обязательно проверяй .успешно.
Перебор словаря. ключ_по_индексу(i) и значение_по_индексу(i) отдают элемент по номеру
от 0 до размер() - 1 — в порядке первой вставки (см. гарантию детерминированности ниже).
Выходить за границы нельзя: проверки диапазона нет. Возвращённый текст принадлежит словарю — не освобождайте его.
пока (i меньше возрасты.размер())
текст имя = возрасты.ключ_по_индексу(i);
целое возраст = возрасты.значение_по_индексу(i);
вывести("%имя%: %возраст%");
i = i + 1;
конец
(Пример целиком: examples/словарь_перебор.код.)
⚠️ Важное синтаксическое ограничение: Список из Т/`Словарь из Ключ и
Значение` нельзя объявить через вывод типа (простое присваивание без
явного типа слева) — всегда указывай тип явно, как в примерах выше.
⚠️ Словарь из Ключ и Значение не участвует в ARC — ни как локальная переменная,
ни как поле структуры, он никогда не освобождается автоматически.
.освободить() — честный, явный способ вернуть память (два внутренних
массива и структуру-заголовок); отдельные текстовые ключи/значения, если
были динамически выделены, освобождает вызывающий код сам, до этого
вызова (язык не может безопасно отличить их от статических строковых
литералов).
Список из T/Словарь из Ключ и Значение можно использовать как поле
пользовательской структуры, с цепочками доступа вида
это.поле.метод(...) — см. пример в структуре Хранилище
(стандартная_библиотека/хранилище.код).
✅ Гарантия детерминированности: несмотря на название, Словарь
НЕ является настоящей хеш-таблицей с бакетами — внутри это два
параллельных массива (ключи и значения, растущие вместе) и линейный
поиск по ним (см. сгенерировать_метод_словаря в src/codegen.rs).
Хеширования, бакетов и какого-либо источника случайности (в духе
защиты от DoS-коллизий у некоторых языков) в реализации нет вовсе —
значит, порядок элементов внутри Словаря всегда РАВЕН порядку их
первой вставки (положить с новым ключом добавляет его строго в
конец) и гарантированно одинаков от запуска к запуску для одной и той
же последовательности операций, без какой-либо зависимости от ASLR,
случайного сида хеширования или платформы. Честно: сегодня в языке
ещё нет способа НАБЛЮДАТЬ этот порядок из кода на Сфере иначе, чем через
ключ_по_индексу/значение_по_индексу (у Словаря нет ни для каждого, ни .ключи()/.значения() — см. выше
и честные ограничения метрики.код/трейсинг.код/профилировщик.код,
которые из-за этого же хранят порядок отдельным Список из текстов) — но
когда такая возможность появится, она унаследует эту детерминированность
БЕСПЛАТНО, если будет реализована как перебор тех же двух массивов по
индексу (а не как переход на настоящую хеш-таблицу с бакетами, что
само по себе было бы серьёзной сменой реализации, а не просто
добавлением одного метода).
Обобщённые структуры (пользовательские)
структура Коробка из Т
Т содержимое;
конец
коробка = Коробка из целое(42); // вывод типа
Коробка из целое другая = Коробка из целое(7); // явный тип — тоже работает
вывести(целое_в_текст(коробка.содержимое));
Раньше явный тип слева (Коробка из целое коробка = ...;) не работал —
семантика не умела сопоставить его с результатом конструктора. Снято:
теперь оба способа объявления равноценны.
Многопараметрические обобщённые структуры (Пара из А и Б) тоже
поддержаны — любое число параметров типа, не только один:
структура Пара из А и Б
А первый;
Б второй;
функция первый_значение(): А
вернуть это.первый;
конец
конец
Пара из целое и текст п = Пара из целое и текст(42, "привет");
вывести(целое_в_текст(п.первый_значение())); // 42
вывести(п.второй); // привет
пара2 = Пара из целое и целое(1, 2); // вывод типа тоже работает
вывести(целое_в_текст(пара2.первый + пара2.второй)); // 3
Параметров может быть и больше двух (структура Тройка из А и Б и Ц ... конец),
и они могут сами быть обобщёнными контейнерами:
структура ПараСписков из А и Б с полями Список из А первые; и
Список из Б вторые;.
Использование с неверным числом параметров — честная ошибка семантики
(не крах и не молчаливое усечение списка), например:
Пара из целое при объявлении Пара из А и Б → `'Пара<...>' объявлена с 2
параметром(ами) типа, а использована с 1 — например: 'Пара<А, Б>'`.
⚠️ **Честно унаследованное ограничение (было и раньше, не новое для
многопараметрических)**: методы шаблона проверяются семантически СРАЗУ
для КАЖДОЙ встреченной инстанциации, независимо от того, вызывается ли
конкретно этот метод на этой инстанциации. Если тело метода годится
только для НЕКОТОРЫХ конкретных типов параметра (например, использует
целое_в_текст(это.поле), а поле подставляется текстом в одной из
инстанциаций), компилятор честно провалится на этой инстанциации, даже
если сам метод там ни разу не вызывается. Обходной путь — не писать в
общем шаблоне методы, зависящие от конкретного типа параметра, либо
использовать разные структуры для разных комбинаций.
Обобщённые функции
функция макс_из_двух из Т (Т а, Т б): Т
если (а больше б)
вернуть а;
конец
вернуть б;
конец
функция главная(): целое
целое итог_целое = макс_из_двух из целое(3, 7); // 7
дробное итог_дробное = макс_из_двух из дробное(2.5, 1.5); // 2.5
вернуть 0;
конец
⚠️ Честные ограничения этой версии:
- Только один обобщённый параметр (как и у обобщённых структур)
- Тип нужно указывать явно при вызове (
имя из тип(...)) — вывод
типа из аргументов без явного указания не реализован
- Только функции главного файла — обобщённые функции из
подключённых модулей пока не поддержаны
Обобщённые методы структур
Отдельный от свободных обобщённых функций метод — метод СТРУКТУРЫ может иметь собственный параметр типа, не связанный с самой структурой:
структура Коробка
целое счётчик;
функция обернуть из Т (Т значение): Т
это.счётчик = это.счётчик + 1;
вернуть значение;
конец
конец
функция главная()
Коробка к = Коробка(0);
целое а = к.обернуть из целое(42); // 42, счётчик = 1
дробное б = к.обернуть из дробное(3.5); // 3.5, счётчик = 2
верно в = к.обернуть из верно(да); // да, счётчик = 3
конец
Внутри обобщённого метода можно вызывать и другие обобщённые методы той
же структуры, в том числе сам себя рекурсивно или из цикла:
это.обернуть из Т(значение).
⚠️ Честные ограничения этой версии (намеренно те же, что и у обобщённых свободных функций):
- Только один обобщённый параметр на метод
- Тип нужно указывать явно при вызове (
объект.метод из тип(...)) —
вывод типа из аргументов не реализован
- **Структура, которой принадлежит метод, сама не должна быть
обобщённой** — комбинация "обобщённая структура + обобщённый метод
внутри неё" (структура Коробка из А с методом функция м из Б (...) внутри) не
поддержана и честно отклоняется ошибкой компиляции. Обычный метод
обобщённой структуры (без своего, ОТДЕЛЬНОГО параметра — опирающийся
только на параметр самой структуры) работает как и раньше, без
ограничений — ограничение именно на ВТОРОЙ, независимый уровень
обобщения.