Добавить в корзинуПозвонить
Найти в Дзене
ZDG

Игра Apple на Rust: модульная структура проекта

В предыдущем выпуске я описал примерную архитектуру игры, и теперь начну её реализовывать. В этом выпуске мы разберёмся с тем, как работают модули. Предыдущие части: Начальное проектирование, Итоги про память, Что там с памятью, Колхозим интерфейсы, Где у него классы, Поддержка SDL2, Полируем ржавчину Напомню, что в Rust нет общепринятых классов, но я использую слово "класс" как неформальное описание структур и типов. Структура проекта Мне импонирует подход к созданию файлов как в Java: один класс – один файл. Так же можно делать и в PHP, и в Python. Когда в одном файле хранится один класс, эти файлы легко искать. Их можно открывать в индивидуальных вкладках и переключаться между ними, не перелистывая код вверх-вниз в поисках нужного класса. Наконец, изменение кода затрагивает только один файл и только один класс, что позволяет не вносить ошибки в другие части кода и распараллеливать работу в команде. Файловая структура проекта могла бы выглядеть так: есть папка game, а в ней есть фай
Оглавление

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

Предыдущие части: Начальное проектирование, Итоги про память, Что там с памятью, Колхозим интерфейсы, Где у него классы, Поддержка SDL2, Полируем ржавчину

Напомню, что в Rust нет общепринятых классов, но я использую слово "класс" как неформальное описание структур и типов.

Структура проекта

Мне импонирует подход к созданию файлов как в Java: один класс – один файл. Так же можно делать и в PHP, и в Python.

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

Файловая структура проекта могла бы выглядеть так: есть папка game, а в ней есть файл GameObject.rs, внутри которого написан код структуры GameObject.

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

Ящики и модули

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

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

Ящик это любой компилируемый файл с расширением .rs. То есть, например, наш основной файл main.rs это ящик. Внутри такого ящика могут содержаться множественные вложения.

В языке C мы используем инструкцию include, чтобы включить в код содержимое другого файла. Причём это включение происходит напрямую – можно считать, что прямо в этом месте появляется текст из другого файла.

В языке PHP совершенно так же работают инструкции include, require и require_once.

В Rust для этого используются модули. Функционал аналогичный: при включении в ящик модуля его текст вставляется прямо в этом месте, и затем всё компилируется целиком.

Но сама организация модулей требует специального подхода.

Я хочу сделать модуль game, чтобы в нём были описаны базовые классы игрового движка: GameObject, Collider, Solver.

Это можно сделать двумя способами:

  • создать файл game.rs
  • создать папку game, а в ней файл mod.rs

В первом случае файлы модулей (допустим, я сделал модули game, graphics, sound) лежат в одном каталоге с файлом main.rs:

Во втором случае создаются папки, а внутри каждой папки свой файл mod.rs:

-2

Хотя это вкусовщина, но подход с папками мне кажется наглядней и чище, т.к. в первом варианте получается свалка файлов.

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

Главная же проблема заключается в том, что описать один класс в одном файле нет возможности. Модуль находится в одном файле, и все классы, которые входят в этот модуль, находятся там же. Чтобы разнести классы по файлам, придётся каждый из них оформлять отдельным модулем, что возможно, но крайне неудобно.

game::GameObject

Начну с модуля game, в который помещу структуру GameObject. По вышеописанной схеме делаю папку game, в которую кладу файл mod.rs со следующим содержанием:

-3

По сравнению с предыдущими примерами можно отметить, что в описание структуры и её полей добавилось ключевое слово pub, то есть "публичный".

Так как структура на этот раз находится внутри модуля, то снаружи доступа к ней нет. В этом отличие модулей от простых вставок текста с помощью include на C и PHP. Чтобы открыть доступ, нужно использовать модификатор pub как на структуре, так и на ее полях.

Для добавления модуля в программу пишем:

mod game;

Теперь можно использовать GameObject в программе:

-4

game::GameObject это путь к необходимой структуре. Сначала идёт имя модуля, затем имя структуры. Если бы модуль содержал подмодуль, то путь мог бы выглядеть так:

game::user::GameObject

и т.д.

Для сокращения путей можно использовать директиву use (аналогично как в PHP):

-5

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

Читайте дальше: Композиция