4.3 Конструирование и конструкторы
Литерал. T{ f1: e1, f2, ..base }:
- поля без значения по умолчанию ДОЛЖНЫ быть заданы, иначе ошибка компиляции;
f2— сокращение дляf2: f2;..baseберёт остальные поля изbase(перемещая или копируя их);- литерал допустим, только если в точке вызова видимы все поля
Tи уTнет атрибута@sealed.
Позиционный литерал. T(e1, e2, …) — поля в порядке объявления; допустим при тех же условиях и если у T не больше 4 полей без значения по умолчанию.
Именованные конструкторы объявляются в impl словом init:
@sealed data Account {
id: AccountId
owner: UserId
balance: Money
invariant balance >= Money.zero()
}
impl Account {
init new(owner: UserId) -> Self =>
Self{ id: AccountId(cluster.ids.next()), owner, balance: Money.zero() }
init open(owner: UserId, deposit: Money) -> Self | TooSmall {
if deposit < MIN_DEPOSIT { fail TooSmall(deposit) }
Self{ ..Self.new(owner), balance: deposit }
}
}
let a = Account.new(me)
let b = Account.open(me, Money.rub(500))?init name(params) -> Self | E— ассоциированная функция с доступом ко всем полям; возвращаемый тип ДОЛЖЕН бытьSelfили объединением, содержащимSelf.@sealedзапрещает литерал внеimplтипа; конструирование только черезinit.T.default()существует, если у всех полей есть значения по умолчанию, или при@derive(Default).- Значения уточнённых типов в литерале подчиняются §3.6:
User{ name: "Ann" }— проверка при компиляции;User{ name: Name.from(s)? }— во время выполнения. - Встроенные поля (§4.7) задаются плоско (
Dog{ name: "Rex", breed }) или целиком по имени типа (Dog{ Animal: a, breed }).
Вызов типа. T(args) разрешается так: если позиционный литерал T допустим в точке вызова — это литерал; иначе, если у T есть init new, — вызов T.new(args); иначе — ошибка компиляции. Для distinct-типа T(x) — конструктор, для шардированного актора — ссылка ActorRef.
Уничтожение. Значение уничтожается при выходе из области видимости владельца в порядке, обратном объявлению. Пользовательский деструктор — impl Drop for T { var fn drop() { … } }; Drop реализуется только явно.
Деструкторы синхронны. Тело drop ДОЛЖНО быть без эффектов и без точек приостановки; допускаются только неблокирующие вызовы освобождения локальных ресурсов ОС, помеченные @nonblocking (закрыть дескриптор, освободить память). Асинхронных деструкторов нет.
Асинхронная очистка — явный метод и defer:
@must_close
data Conn { sock: Socket; open: bool = true }
impl Conn {
move fn close() -> () | NetError net {
shield { sock.sendGoAway()? } // отмена не прерывает завершение
open = false
}
}
impl Drop for Conn {
var fn drop() {
if open { // close() забыли: страховка без сети
sock.closeLocal()
log.warn("Conn dropped without close()")
}
}
}
fn talk(addr: Addr) -> () | NetError net {
let c = Conn.dial(addr)?
defer c.close()? // ошибка close() возвращается из функции
c.send(HELLO)?
}@must_closeна типе: если значение может выйти из области видимости без вызоваmove-метода, помеченного как закрывающий (по умолчаниюclose, иначе@must_close(by: имя)), или без передачи владения, — ошибка компиляцииmust-close.defer e?внутри функции с ошибками: ошибкаeвозвращается из функции, если функция ещё не возвращает другую ошибку; иначе пишется в лог.- Синхронный
Dropостаётся страховкой на случай паники и отмены: он освобождает локальные ресурсы и пишет предупреждение, но в сеть не ходит.
Связанные диагностики
bad-init, missing-field, must-close, positional-literal, sealed-literal
См. также
Исходник: docs/spec/oxl-standard.md