Раздел 41 · Zig с нуля

Дженерики через comptime-типы

junior~10 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Дженерики через comptime-типы

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

Напишем операцию выбора максимума для целых и дробных чисел. Она не будет хранить «любой объект» в загадочной общей коробке. Каждый вызов получит конкретный тип.

Одна мысль урока

Тип может быть comptime-параметром, по которому строится конкретный вариант функции.

Опора из прошлого урока: comptime: код на этапе компиляции.

От картинки к записи

Обобщённый код

часто называют дженериком. В Zig его можно выразить функцией с comptime T: type. Значение типа type само представляет другой тип. Параметр T известен компилятору, поэтому остальные параметры могут иметь тип T.

Сигнатура maximum(comptime T: type, a: T, b: T) T читается последовательно: сначала выбрать тип, затем получить два значения этого типа, затем вернуть значение того же типа. Вызов maximum(u16, 3, 9) не передаёт строку "u16", он передаёт значение типа. Кавычки изменили бы смысл и сделали аргумент несовместимым.

Для u16 компилятор строит вариант с целочисленным сравнением. Для f64 вариант с дробными числами. Это не означает, что во время каждого вызова проверяется имя типа. Тип выбирается при компиляции, а числовые аргументы могут прийти позже. Раннее знание T не требует раннего знания a и b.

Функция подходит только типам, для которых определено сравнение >. Если передать неподходящую структуру, компилятор покажет ошибку в операции. Позже научимся заранее проверять свойства типа и давать более понятное сообщение. Сейчас важно честно ограничить договор: используем числа, не обещаем универсальный максимум любых данных.

Можно возвращать тип из функции. Например, fn Pair(comptime T: type) type возвращает структуру с двумя полями T. Так устроены многие коллекции стандартной библиотеки: std.ArrayList(u8) представляет конкретный тип списка байтов. Скобки здесь вызывают функцию над типом, а не вводят синтаксис другого отдельного языка.

Есть форма anytype, которая выводит тип аргумента из вызова. Она полезна, но скрывает часть договора из сигнатуры. Для первого знакомства явный T лучше: видно, откуда взялись типы входов и результата. Не смешивай разные типы в одном вызове maximum без осознанного преобразования.

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

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

премиум-урок

Продолжение в премиуме: 1 200 ₽ в месяц или 10 000 ₽ в год. Вместе с ним серверные проверки задач и школьные токены AI-коуча.

Оплата пока не подключена: премиум и курсы открываются промокодом.