Перейти к содержанию

Массивы в Solidity: ограничения и пагинация

После getter по одному номеру возникает желание показать несколько курсов за один запрос. Это удобно для интерфейса, но массив не должен превращаться в разрешение на произвольный объём работы. Разберём ограниченное чтение выбранных номеров и сравним его с попыткой вернуть весь каталог.

Функция ниже — дополнительный учебный getter, а не замена базовых методов изменения. Она получает явный список номеров, возвращает текущие записи в том же порядке и ограничивает длину двадцатью элементами. Предел выбран для упражнения, а не измерен как идеальное значение для любой сети.

Вход и соответствие результата

error TooManyCourses();

function currentMany(uint256[] calldata ids)
    external
    view
    returns (Course[] memory result)
{
    if (ids.length > 20) revert TooManyCourses();
    result = new Course[](ids.length);
    for (uint256 i; i < ids.length; ++i) {
        result[i] = courses[ids[i]];
    }
}

Фрагмент добавляется внутрь отдельного варианта реестра с прежними Course и courses. Мы не получаем список ключей из mapping: вызывающий участник сам передаёт известные номера. Результат имеет тот же размер. Если переданы [17,18], первый элемент относится к 17, второй — к 18.

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

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

Откуда берётся граница

Цикл зависит от длины входа. Без предела вызывающий участник мог бы попросить очень длинный набор. Даже view-чтение требует вычисления на узле, создания памяти и кодирования ответа. Некоторые RPC ограничивают работу и размер результата. А другой контракт может вызвать getter в своей транзакции, где расход становится частью газа.

Проверка ids.length > 20 позволяет объяснить верхнюю границу числа итераций. Это не гарантия подходящей стоимости всех будущих изменений структуры. Если Course затем получает динамический массив из тысячи элементов, двадцать записей могут стать огромным ответом. Ограничение числа записей и ограничение внутреннего размера каждого элемента — разные требования.

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

Страница каталога на сайте

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

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

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

Пустой вход и слишком длинный вход

Пустой массив в этом договоре даёт пустой результат. Это удобный нейтральный случай: отсутствие номеров не является ошибкой. Если проект хочет обязательное наличие хотя бы одного курса, нужно отдельно добавить такое условие. Размер new Course[](0) не означает публикации пустого курса.

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

Вход Ожидаемый результат
[] Пустой массив
[17] Одна текущая запись
[17,18] Две записи с сохранённым порядком
[17,17] Две копии ответа для того же номера
21 номер TooManyCourses

Почему не нужен бесконечный getter

Функция «все записи от первого до последнего номера» имеет сразу несколько проблем. Номера могут иметь пропуски и большой диапазон. Mapping не сообщает максимальный использованный ключ. Даже отдельный массив ключей способен расти без границ. В какой-то момент ответ перестанет быть удобным или доступным.

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

Мы подготовили понятный групповой getter с явным входом, размером и соответствием ответа. Его ограничения объясняются работой памяти и узла, а не произвольной модой на страницы по двадцать элементов. Массивы Solidity помогают сверить выделение памяти и тип результата.

Предел и пользовательская страница

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

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

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

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

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

Оглавление курса. Листинги, расчёты и наблюдения здесь разобраны как ожидаемое поведение; компиляция, учебные программы и проверки при написании не запускались.

Оглавление серии