V.3 Конструкторы, интерфейсы, встраивание, операторы
// Деньги храним в копейках. @repr(i64): по сети и в БД Money выглядит как одно число.
// @derive генерирует копирование, сравнение, хеш и вывод в строку.
@repr(i64) @derive(Copy, Eq, Ord, Hash, Show)
pub(api) data Money { minor: i64 }
impl Money {
pub const ZERO: Money = Money{ minor: 0 } // ассоциированная константа
static fn rub(r: i64) -> Money => Money{ minor: r * 100 } // static: вызывается как Money.rub(…)
static fn zero() -> Money => Money.ZERO
// Перегрузка операторов: теперь можно писать a + b, a * 3
static op +(a: Money, b: Money) -> Money => Money{ minor: a.minor + b.minor }
static op -(a: Money, b: Money) -> Money => Money{ minor: a.minor - b.minor }
static op *(a: Money, k: i64) -> Money => Money{ minor: a.minor * k }
static op /(a: Money, k: i64) -> Money => Money{ minor: a.minor / k }
}
// Как превращать Money в i64 и обратно при сериализации (§9.5)
impl Repr<i64> for Money {
fn toRepr() -> i64 => minor
static fn fromRepr(v: i64) -> Money | DecodeError => Money{ minor: v }
}
// Интерфейс структурный: подходит любой тип с методом price()
interface Priced { fn price() -> Money }
data Product { sku: Sku; title: str; base: Money }
impl Product {
open fn price() -> Money => base // open — метод можно переопределить во встраивающем типе
}
type Percent = u8 where 0..=90 // уточнённый тип: значение проверяется при создании
// @sealed: литерал Discounted{…} снаружи запрещён, создать можно только через init
@sealed data Discounted {
embed Product // встраивание: поля и методы Product доступны напрямую
percent: Percent
}
impl Discounted {
// Именованный конструктор. Percent.from проверяет диапазон и может вернуть InvalidValue.
init of(p: Product, percent: u8) -> Self | InvalidValue =>
Self{ Product: p, percent: Percent.from(percent)? }
// override обязателен; super.price() — версия из Product
override fn price() -> Money => super.price() - super.price() * (percent as i64) / 100
}
// [dyn Priced] — срез значений разных типов, вызов price() через vtable
fn total(items: [dyn Priced]) -> Money pure =>
items.fold(Money.ZERO, (acc, i) => acc + i.price())
let d = Discounted.of(book, 15)? // ? пробрасывает InvalidValue, если процент вне 0..=90
assert d.price() == Money.rub(850) // при book.base = Money.rub(1000)Discounted удовлетворяет Priced через продвинутый и переопределённый price. Литерал Discounted{ … } вне impl запрещён (@sealed), поэтому процент всегда проверен.
Исходник: docs/spec/oxl-standard.md