Потребляемые типы в Nova
Руководство для пользователя по работе с потребляемыми типами: связывания, передача владения, заём-представление, жизненный цикл.
TL;DR
consume x = Token.new(7) // ✓ ownership binding
ro y = x // ✗ E_VIEW_BINDING_FORBIDDEN
consume y = x // ✓ move (x dead, y owns)
ro v = x.release() // ✓ consume via method
- Владение передаётся через
consume X = …. - Связывание-псевдоним запрещено внутри тела функции (
let Y = X). - Заём-представление (view-borrow) разрешён ТОЛЬКО как параметр функции.
- Каждое consume-связывание должно быть потреблено до выхода из области видимости.
Что такое потребляемый тип?
Потребляемый тип — это тип, чьи значения представляют владение неразделяемым ресурсом: файловым дескриптором, охраной мьютекса, буфером строителя (builder), сетевым сокетом. Значения нельзя копировать, им нельзя создавать псевдонимы или неявно отбрасывать; владелец обязан явно их потреблять.
Объявление:
type Token consume {
val int
}
Ключевое слово consume в объявлении типа помечает все экземпляры как
обязанные быть потреблёнными.
Правила связывания (D180)
Правило 1 — для consume-RHS обязательно consume X = …
ro t = Token.new(7) // ✗ E_CONSUME_KEYWORD_MISSING
consume t = Token.new(7) // ✓
Компилятор статически определяет, когда связывание получает значение
потребляемого типа, и требует ключевое слово consume.
Правило 2 — let Y = consume_var запрещено в теле функции
consume sb = StringBuilder.new()
ro view = sb // ✗ E_VIEW_BINDING_FORBIDDEN
Создание псевдонима для обязательства потребления привело бы к висячей
ссылке после того, как sb будет потреблён; связывание-псевдоним внутри
тел функций запрещено.
Правило 3 — consume Y = X передаёт владение (move)
consume a = Token.new(11)
consume b = a // move — a dead, b owns
ro v = b.release() // ✓
После перемещения a считается потреблённым; его использование вызывает
диагностику use-after-consume.
Правило 4 — заём-представление только через параметры функции
fn snapshot(t Token) -> int => t.val
consume t = Token.new(7)
ro s = snapshot(t) // ✓ view-borrow (bounded by call)
ro v = t.release() // ✓ caller still owns; consume here
Параметры функции потребляемого типа без ключевого слова consume — это
представления (views) — ограниченные областью видимости вызываемой функции,
никогда не убегают наружу.
Операции потребления
Обязательство потребления погашается любым из способов:
consume X = X_old— перемещение в новое связывание (правило 3).X.method(...), где метод объявлен какconsume @-метод.return X— возврат из функции.- Неявный возврат — завершающее выражение тела функции это
X(или цепочка вызовов с корнем вX; см. [M-73.1-fluent-return- implicit-consume]). - Передача в consume-параметр —
f(X), гдеfпринимаетconsume X.
Цепочки вызовов с возвратом получателя (-> @)
Метод, объявленный с типом возврата -> @, возвращает самого получателя
(Plan 77, D132). Цепочки вызовов компонуют мутаторы:
consume sb = StringBuilder.new()
ro s = sb.append("a").append("b").as_str() // chain + consume
Когда такая цепочка является завершающим выражением тела функции, обязательство потребления корня цепочки погашается неявным возвратом (M3, Plan 73.1 V3).
Диагностики
| Код | Триггер |
|---|---|
E_CONSUME_KEYWORD_MISSING | let X = ctor(), когда ctor возвращает consume-тип |
E_VIEW_BINDING_FORBIDDEN | let Y = consume_var в теле функции |
W_CONSUME_KEYWORD_UNNECESSARY | consume X = …, когда правая часть не consume-типа |
D133-not-consumed | выход из области видимости с неудовлетворённым обязательством |
D133-use-after-consume | использование потреблённого связывания |
Все диагностики несут машинно-применимые предложения (Plan 50 D102).