Определите, каким именам можно присваивать новые значения и где эти имена видны. Затем проследите за счётчиком, который меняется при каждом вызове возвращённой функции.
Установите Топаз по инструкции Первая программа. Основы let и функций разобраны в уроке Значения и функции.
Проследите за счётчиком
Сохраните этот код в файле bindings-scope-closures.tpz в рабочей папке.
function создатьСчётчик(начало: int) -> () -> int {
let mut текущее = начало
() => {
текущее = текущее + 1
текущее
}
}
let метка = "outer"
let следующий = создатьСчётчик(10)
{
let метка = "inner"
print("{метка}: {следующий()}, {следующий()}")
}
print(метка)В той же папке проверьте файл и запустите его.
topaz check bindings-scope-closures.tpz
topaz run bindings-scope-closures.tpzПроверка завершается с кодом 0. Программа выводит следующие две строки.
inner: 11, 12
outerРазберите два вызова
Привязка связывает имя со значением. let mut текущее = начало создаёт привязку, которой можно присвоить новое значение. Вызов создатьСчётчик(10) задаёт начальное значение 10 и возвращает безымянную функцию, записанную через =>. Такая функция называется лямбдой.
Лямбда сохраняет доступ к текущее из окружающей функции. Это захват, а функция, сохраняющая такой доступ, называется замыканием. После возврата из создатьСчётчик это замыкание хранится в следующий.
| Шаг | Значение текущее | Результат |
|---|---|---|
создатьСчётчик(10) | 10 | Замыкание |
Первый вызов следующий() | 11 | 11 |
Второй вызов следующий() | 12 | 12 |
Оба вызова используют одну привязку. Второй прибавляет единицу к 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 и ресурсы. Выбрать именованную функцию, лямбду или обобщённую функцию поможет раздел Функции и обобщения.