Основы языка

Привязки, области видимости и замыкания

Выберите let или let mut, проследите за именами в блоках и за счётчиком при повторных вызовах замыкания.

Определите, каким именам можно присваивать новые значения и где эти имена видны. Затем проследите за счётчиком, который меняется при каждом вызове возвращённой функции.

Установите Топаз по инструкции Первая программа. Основы let и функций разобраны в уроке Значения и функции.

Проследите за счётчиком

Сохраните этот код в файле bindings-scope-closures.tpz в рабочей папке.

TOPAZ
function создатьСчётчик(начало: int) -> () -> int {
    let mut текущее = начало
    () => {
        текущее = текущее + 1
        текущее
    }
}

let метка = "outer"
let следующий = создатьСчётчик(10)

{
    let метка = "inner"
    print("{метка}: {следующий()}, {следующий()}")
}

print(метка)

В той же папке проверьте файл и запустите его.

BASH
topaz check bindings-scope-closures.tpz
topaz run bindings-scope-closures.tpz

Проверка завершается с кодом 0. Программа выводит следующие две строки.

Вывод
inner: 11, 12
outer

Разберите два вызова

Привязка связывает имя со значением. let mut текущее = начало создаёт привязку, которой можно присвоить новое значение. Вызов создатьСчётчик(10) задаёт начальное значение 10 и возвращает безымянную функцию, записанную через =>. Такая функция называется лямбдой.

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

ШагЗначение текущееРезультат
создатьСчётчик(10)10Замыкание
Первый вызов следующий()1111
Второй вызов следующий()1212

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

Найдите имя, которое использует блок

Область видимости определяет, в какой части кода доступно имя. В Топазе она лексическая. Объявление имени и окружающие блоки определяют, к какой привязке оно относится.

Внутреннее объявление let метка скрывает внешнюю привязку внутри своего блока. Это затенение имени. Первый print читает "inner". За пределами блока print(метка) читает "outer". Два объявления одного имени в одной области видимости вызывают статическую ошибку.

Выберите возможность присваивания

ФормаКогда применятьПовторное присваивание
constЗначение, вычисляемое при компиляции из допустимого константного выраженияЗапрещено
letИмя, сохраняющее присвоенное значениеЗапрещено
let mutСчётчик или другое состояние, которому присваивают новые значенияРазрешено

Начните с let и добавьте mut, если понадобится повторное присваивание. Параметры и имена из образцов неизменяемы. Чтобы менять значение параметра локально, присвойте его новой привязке let mut. Так создатьСчётчик использует параметр начало.

Если убрать mut из объявления текущее, присваивание внутри лямбды вызовет статическую ошибку. Восстановите let mut текущее в файле bindings-scope-closures.tpz, чтобы счётчик снова работал. Захват сохраняет возможность изменения исходной привязки.

Правила захвата и объявления

  • Замыкание захватывает имена, видимые в месте его создания. Имена, объявленные позже или внутри соседних блоков, недоступны.
  • Изменяемое объявление использует один идентификатор. Образец деструктурирования не может вводить изменяемые привязки.
  • Неизменяемому let, const, параметру или привязке образца нельзя присвоить новое значение и внутри замыкания.
  • Явные списки захвата, захват с перемещением, трейты замыканий Rust и синтаксис заимствующих получателей не поддерживаются.

Время жизни захваченной привязки не определяет момент закрытия файла. Форма using для File и правила очистки описаны в разделе Defer и ресурсы. Выбрать именованную функцию, лямбду или обобщённую функцию поможет раздел Функции и обобщения.