Уроки · 37
Свои структуры
В уроке «Разноцветные кружки» каждый кружок — это кортеж из
трёх элементов: (f32, f32, Color). Добавим кружкам ещё и радиус —
четвёртый элемент, (f32, f32, f32, Color).
У элементов кортежа есть номера — .0, .1, .2, .3 — и достать
элемент по номеру можно прямо через точку:
let circle: (f32, f32, f32, Color) = (120.0, 80.0, 30.0, YELLOW);
// .0, .1, .2, .3 — элементы кортежа по порядку.draw_circle(circle.0, circle.1, circle.2, circle.3);Можно вместо этого разобрать кортеж на отдельные переменные — так,
как мы всегда делали с mouse_position():
let (x, y, radius, color) = circle;draw_circle(x, y, radius, color);Читается лучше, но разбирать приходится заново на каждом месте, где
кортеж используется — а в уроке «Список кружков» таких мест
несколько: и когда кружок только создаётся, и в цикле, где рисуется
каждый уже сохранённый. С парой чисел это было не в тягость. С
четырьмя элементами — уже утомительно, и легко перепутать местами
два поля одного типа при разборе: например, radius и x, оба
f32, компилятор такую ошибку даже не заметит.
struct — свой именованный тип
Заголовок раздела «struct — свой именованный тип»Хочется чего-то среднего: чтобы поля были видны по имени, но разбирать кортеж заново каждый раз не приходилось — сразу пользоваться именем поля, как у массива есть индекс, доступный прямо на месте, без подготовки. Кортеж для этого не подходит: у него просто нет имён для полей, только номера. В Rust имена полей нельзя придумать на ходу, прямо в месте, где создаётся значение — сначала нужно объявить форму структуры отдельно, один раз, а уже потом создавать по ней значения:
struct Circle { x: f32, y: f32, radius: f32, color: Color,}Разберём по кусочкам. struct — ключевое слово, как let и fn:
Rust понимает его как «дальше будет новый тип данных», и это слово
тоже зарезервировано языком, не наше. После него — имя типа, здесь
Circle (по соглашению с большой буквы, в отличие от имён переменных
и функций). А в фигурных скобках — поля: у каждого сначала имя, потом
через двоеточие тип, через запятую, если полей несколько, — та же
запись имя: тип, что и у параметров функции в уроке «Свои
функции», только здесь она описывает не вход функции,
а форму будущих значений.
Значение такого типа создаётся по имени, с явным указанием, что куда идёт:
let circle = Circle { x: 120.0, y: 80.0, radius: 30.0, color: YELLOW };
// Каждое поле теперь видно по имени, а не по номеру.draw_circle(circle.x, circle.y, circle.radius, circle.color);Color тут — тот же тип, что и в уроке «Разноцветные
кружки»: тип значений вроде YELLOW и RED.
Попробуй сам
Заголовок раздела «Попробуй сам»Кликай по экрану — на месте клика появляется кружок со случайным радиусом и случайным цветом из того же набора, что и в уроке «Разноцветные кружки».
Замени содержимое src/main.rs на это:
use macroquad::prelude::*;
// Фиксированный список цветов, из которого выбираем случайный.const COLORS: [Color; 4] = [YELLOW, RED, GREEN, BLUE];
// Один кружок: положение, радиус и цвет вместе, под понятными именами.struct Circle { x: f32, y: f32, radius: f32, color: Color,}
#[macroquad::main("Свои структуры")]async fn main() { // Список уже нарисованных кружков — сначала пустой. let mut circles: Vec<Circle> = Vec::new();
loop { clear_background(BLACK);
// Клик — новый кружок со случайным радиусом и случайным цветом. if is_mouse_button_pressed(MouseButton::Left) { let (x, y) = mouse_position(); let radius = rand::gen_range(10.0, 60.0); let color = COLORS[rand::gen_range(0, COLORS.len())]; circles.push(Circle { x, y, radius, color }); }
// Рисуем все уже сохранённые кружки. for circle in &circles { draw_circle(circle.x, circle.y, circle.radius, circle.color); }
next_frame().await; }}Запусти как обычно:
cargo runПопробуй прямо здесь:
Что здесь происходит
Заголовок раздела «Что здесь происходит»struct Circle { ... }объявляет новый тип данных с именованными полями — свою собственную структуру, а не встроенную, какf32илиColor.Circle { x: ..., y: ..., radius: ..., color: ... }создаёт значение этого типа — поля перечисляются по имени, порядок не важен.circle.radius,circle.color— доступ к полю по имени, вместо.0,.1,.2,.3у кортежа, и без того, чтобы сначала разбирать всю структуру на отдельные переменные — поле берётся прямо там, где нужно.radiusвCircle { ..., radius, ... }— сокращённая запись: раз локальная переменная называется так же, как поле,radius: radiusможно не писать целиком.rand::gen_range(10.0, 60.0)— случайный радиус от 10 до 60, та же функция, что уже выбирала случайный цвет и случайную точку («Случайная точка»), только диапазон теперь не индекс массива, а сам радиус.Vec<Circle>— тот же растущий список из урока «Список кружков», просто элементы теперь — не кортежи, а значенияCircle.