Перейти к содержимому

Уроки · 18

Где живёт переменная

В уроке «Изменяемые переменные» мы увидели: если x объявить внутри loop, она создаётся заново каждый кадр и теряет накопленное значение, а если до loop — живёт и копит значение всё время игры. mut разрешает менять значение, пока переменная жива, но не решает, сколько именно она живёт. Разберёмся, почему так — дело в границах, которые задают фигурные скобки { }.

Фигурные скобки { } — это и есть область видимости. Как только выполнение доходит до закрывающей скобки, всё, что было объявлено внутри, уничтожается. В следующий раз, когда этот блок выполнится снова (например, на следующем проходе loop), переменные внутри него создаются заново, с нуля, по своей инициализации — как будто в первый раз.

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

use macroquad::prelude::*;
#[macroquad::main("Движение")]
async fn main() {
loop {
clear_background(BLACK);
// x объявлена внутри loop — создаётся заново на каждом кадре.
let mut x = screen_width() / 2.0;
// Стрелка вправо зажата — двигаем x.
if is_key_down(KeyCode::Right) {
x += 8.0;
}
draw_circle(x, screen_height() / 2.0, 60.0, YELLOW);
next_frame().await;
}
}

Зажми стрелку вправо — и кружок сдвинется на 8 пикселей от центра и дальше замрёт на месте, сколько бы ни держал клавишу. x здесь создаётся заново каждый кадр внутри loop, поэтому в конце кадра она уничтожается вместе со своим значением — а на следующем кадре появляется новая x, опять ровно по центру. mut тут не спасает: он разрешает менять x, пока она жива, — то есть в пределах одного кадра, — но к следующему кадру это уже совсем другая переменная.

use macroquad::prelude::*;
#[macroquad::main("Движение")]
async fn main() {
// x объявлена до loop — создаётся один раз и живёт всё время игры.
let mut x = screen_width() / 2.0;
loop {
clear_background(BLACK);
// Стрелка вправо зажата — двигаем x.
if is_key_down(KeyCode::Right) {
x += 8.0;
}
draw_circle(x, screen_height() / 2.0, 60.0, YELLOW);
next_frame().await;
}
}

Теперь x создаётся один раз, ещё до первого прохода loop, и с тех пор просто живёт. Каждый x += 8.0 прибавляется к той же самой x, что была на прошлом кадре, — поэтому движение накапливается, и кружок едет дальше и дальше, а не дёргается на 8 пикселей и замирает.

  • { } у loop — это и есть область видимости: всё, что объявлено прямо внутри неё, живёт один проход и уничтожается вместе с его концом.
  • Вложенный блок if { ... } внутри loop видит x, даже если она объявлена ещё до loop, — блок глубже всегда видит переменные снаружи себя.
  • Значит, чтобы переменная копила значение между кадрами (как x здесь, или x/y в уроке «Движение»), объявлять её нужно снаружи loop — тогда область её видимости шире одного кадра.