RFC-011a: Реализация интерфейсов и динамическая диспетчеризация
Родительский RFC: RFC-011: Проектирование системы обобщённых типов
Данный RFC дополняет и заменяет части §2.1-2.4 RFC-011, касающиеся ограничений интерфейсов.
Краткое описание
RFC-011 определил систему обобщённых типов, но не детализировал механизм реализации интерфейсов. Этот документ дополняет:
- Объявление интерфейса: имя интерфейса записывается непосредственно в определении типа, без ключевого слова
impl - Реализация методов: поддерживаются как внутренние, так и внешние объявления
- Правила перегрузки: разные сигнатуры разрешены, одинаковые сигнатуры вызывают ошибку (перекрытие запрещено)
- Значения по умолчанию:
= valueзаписывается сразу после поля - Динамическая диспетчеризация: сбор типов на этапе компиляции + сопоставление интерфейсов, без виртуальной таблицы
Основной дизайн:
# Определение интерфейса
Animal: Type = {
speak: (Self) -> String,
}
# Определение типа (внутреннее объявление)
Dog: Type = {
x: Int = 10,
Animal, # Объявление интерфейса
speak: (Self) -> String = "Woof",
}
# Внешнее объявление (перегрузка)
Dog.speak: (Self, volume: Int) -> String = "WOOF"
# Гетерогенный контейнер (динамическая диспетчеризация)
animals: List(Animal) = [Dog.new(), Cat.new()]
animals[0].speak() # "Woof"Устранённая сложность:
- ❌ Нет ключевого слова
impl - ❌ Нет аннотации
dyn Trait + 'a - ❌ Нет виртуальной таблицы (сбор типов на этапе компиляции + enum-обёртка)
- ❌ Нет перекрытия (единые правила перегрузки)
Мотивация
Недостатки RFC-011
RFC-011 определил систему обобщённых типов, но не детализировал:
| Проблема | Описание |
|---|---|
| Синтаксис объявления интерфейса | Как объявить, что тип реализует интерфейс? |
| Расположение реализации методов | Внутреннее или внешнее объявление? |
| Правила перегрузки | Как обрабатывать одноимённые методы? |
| Синтаксис значений по умолчанию | Как задать значение по умолчанию для поля? |
| Динамическая диспетчеризация | Как реализовать гетерогенные контейнеры? |
Цели проектирования
- Простота: не требуется ключевое слово
impl - Гибкость: реализация методов поддерживается внутри или снаружи
- Единство: правила перегрузки согласованы
- Удобство: лаконичный синтаксис значений по умолчанию
- Нулевые накладные расходы: без виртуальной таблицы, сбор типов на этапе компиляции
Сравнение с Rust
| Характеристика | Rust | YaoXiang |
|---|---|---|
| Объявление интерфейса | impl Animal for Dog { ... } | Dog: Type = { Animal, ... } |
| Реализация методов | В блоке impl | Внутри или снаружи |
| Перегрузка | Не поддерживается | Поддерживается (разные сигнатуры) |
| Значения по умолчанию | Требуется #[default] | Записывается напрямую = value |
| Гетерогенный контейнер | Vec<Box<dyn Animal + 'a>> | List(Animal) |
| Динамическая диспетчеризация | Поиск в виртуальной таблице | Сбор типов на этапе компиляции |
Предложение
1. Объявление интерфейса
Основное правило: имя интерфейса записывается непосредственно в определении типа, без ключевого слова impl.
# Определение интерфейса
Animal: Type = {
speak: (Self) -> String,
}
# Объявление типа реализует интерфейс
Dog: Type = {
x: Int,
Animal, # Объявление интерфейса
}Обработка компилятором:
- Распознать
Animalкак тип интерфейса - Проверить, есть ли у
Dogвсе методы, требуемыеAnimal - Если проходит → сгенерировать доказательство реализации
- Если не проходит → ошибка компиляции
Эквивалентный синтаксический сахар:
Dog: Type = {
x: Int,
Animal, # Эквивалентно раскрытию методов Animal, но с сохранением метки источника
}
# Эквивалентно (но с сохранением информации об источнике)
Dog: Type = {
x: Int,
speak: (Self) -> String, # Из Animal
}Зачем нужна метка источника:
- Прямое раскрытие теряет информацию об источнике
- Метка источника используется для генерации доказательства реализации
- Во время выполнения по доказательству находится правильный метод
2. Реализация методов
Основное правило: реализация методов поддерживается как во внутренних, так и во внешних объявлениях.
2.1 Внутреннее объявление
Dog: Type = {
x: Int = 10,
Animal,
speak: (Self) -> String = "Woof", # Реализация метода внутри
}2.2 Внешнее объявление
Dog: Type = {
x: Int,
Animal,
}
# Реализация метода снаружи
Dog.speak: (Self) -> String = "Woof"2.3 Смешанное объявление
Dog: Type = {
x: Int = 10,
Animal,
speak: (Self) -> String = "Woof", # Часть методов внутри
}
# Часть методов снаружи
Dog.play: (Self) -> Void = { ... }Обработка компилятором:
- Собрать все определения (внутренние и внешние)
- Сгруппировать по сигнатурам (перегрузка)
- Проверить наличие перекрытия (ошибка)
- Проверить полноту интерфейса
- Сгенерировать доказательство реализации
3. Перегрузка и перекрытие
Основные правила:
- Разные сигнатуры → перегрузка → разрешено
- Одинаковые сигнатуры → перекрытие → ошибка
3.1 Перегрузка (разрешено)
# Разные типы параметров, перегрузка разрешена
Dog.speak: (Self) -> String = "Woof"
Dog.speak: (Self, volume: Int) -> String = "WOOF"3.2 Перекрытие (запрещено)
# Полностью одинаковые сигнатуры, перекрытие запрещено
Dog.speak: (Self) -> String = "Woof"
Dog.speak: (Self) -> String = "Bark" # ❌ Ошибка: перекрытие не разрешеноСообщение об ошибке:
Ошибка: Dog.speak(Self) -> String повторное определение
--> файл2:5:1
|
5 | Dog.speak: (Self) -> String = "Bark"
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ повторное определение
|
--> файл1:3:1
|
3 | Dog.speak: (Self) -> String = "Woof"
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ первое определение3.3 Единые правила
Внутренние и внешние объявления следуют одним и тем же правилам перегрузки/перекрытия:
# Внутреннее объявление
Dog: Type = {
x: Int,
Animal,
speak: (Self) -> String = "Woof",
}
# Внешнее объявление (перегрузка, разрешено)
Dog.speak: (Self, volume: Int) -> String = "WOOF"
# Внешнее объявление (перекрытие, запрещено)
Dog.speak: (Self) -> String = "Bark" # ❌ Ошибка4. Значения по умолчанию
Основное правило: = value записывается сразу после поля, без конструктора.
Dog: Type = {
x: Int = 10, # Значение по умолчанию
y: Int = 20, # Значение по умолчанию
Animal,
}Конструктор, сгенерированный компилятором:
# Все поля со значениями по умолчанию → генерируется конструктор без параметров
Dog.new: () -> Dog = { x: 10, y: 20 }
# Часть полей со значениями по умолчанию → генерируются конструкторы с частичными параметрами
Dog.new: (x: Int) -> Dog = { x: x, y: 20 }
Dog.new: (y: Int) -> Dog = { x: 10, y: y }
# Конструктор со всеми параметрами
Dog.new: (x: Int, y: Int) -> Dog = { x: x, y: y }Внешнее объявление значений по умолчанию:
Dog: Type = {
x: Int,
y: Int,
Animal,
}
# Внешнее объявление значений по умолчанию
Dog.x: Int = 10
Dog.y: Int = 20Эквивалентно внутреннему объявлению.
5. Реализация компилятора
5.1 Дескриптор интерфейса
// Внутренняя структура компилятора: дескриптор интерфейса
struct InterfaceDescriptor {
name: String,
methods: Vec<MethodSignature>,
}5.2 Определение типа
// Внутренняя структура компилятора: определение типа
struct TypeDefinition {
name: String,
fields: Vec<Field>,
interface_implementations: Vec<InterfaceImplementation>,
}
// Реализация интерфейса (с сохранением информации об источнике)
struct InterfaceImplementation {
interface: InterfaceId,
methods: HashMap<MethodId, FunctionBody>,
}5.3 Доказательство реализации
// Внутренняя структура компилятора: доказательство реализации
struct ImplementationProof {
type_id: TypeId,
interface_id: InterfaceId,
methods: Vec<MethodPointer>,
}5.4 Процесс компиляции
1. Парсинг определения типа, сбор объявлений интерфейсов
2. Сбор всех определений методов (внутренних и внешних)
3. Группировка по сигнатурам (перегрузка)
4. Проверка перекрытия (ошибка)
5. Проверка полноты интерфейса
6. Генерация доказательства реализации
7. Во время выполнения значение несёт доказательство реализации6. Динамическая диспетчеризация
Основной дизайн: сбор типов на этапе компиляции + сопоставление интерфейсов, без виртуальной таблицы.
6.1 Гетерогенный контейнер
# Определение интерфейса
Animal: Type = {
speak: (Self) -> String,
}
# Определение типа
Dog: Type = {
x: Int,
Animal,
speak: (Self) -> String = "Woof",
}
Cat: Type = {
y: Int,
Animal,
speak: (Self) -> String = "Meow",
}
# Гетерогенный контейнер
animals: List(Animal) = [Dog.new(), Cat.new()]
animals[0].speak() # "Woof"
animals[1].speak() # "Meow"6.2 Сбор типов на этапе компиляции
Основная стратегия: отслеживание принадлежности, инкрементное построение. Не производится сканирование всех типов, реализующих интерфейс, на этапе компиляции — вместо этого на каждой точке владения List(Animal) производится инкрементный сбор:
// Точка конструирования
animals: List(Animal) = [Dog.new()] // AnimalGroup = { Dog(Dog) }
// Точка append
animals.append(Cat.new()) // Компилятор в точке append видит Cat → расширяет до { Dog, Cat }
animals.append(Bird.new()) // Далее расширяет { Dog, Cat, Bird }Обработка компилятором (инкрементная):
- Первый раз встречен
List(I)при конструировании → генерируется начальный enum (все известные типы в текущей единице компиляции) - При каждом
append/push/ присваивании по индексу → проверяется, есть ли тип значения уже в enum; если нет — расширяется enum вариант - Для финального enum генерируется мономорфизированный код диспетчеризации
match - Кросс-единицы компиляции: на этапе линковки объединяются наборы вариантов enum из разных единиц
Автоматически сгенерированный enum:
# Компилятор генерирует автоматически (пользователь не замечает)
AnimalGroup: Type = {
Dog(Dog),
Cat(Cat),
Bird(Bird), # ← append(Bird.new()) вызывает инкрементное расширение
}
# Внутри List(Animal) эквивалентен List(AnimalGroup)6.3 Проверка сопоставления интерфейса
Ключевой инсайт: сопоставление интерфейса — это проверка на этапе компиляции, даже если типы загружаются из динамически подключаемых плагинов.
# Система плагинов
plugin = load_plugin("bird.so")
# Компилятор проверяет: тип, возвращаемый plugin.create_bird(), должен реализовывать Animal
bird: Animal = plugin.create_bird() # Проверка на этапе компиляции
# Помещение в гетерогенный контейнер —— точка append вызывает расширение enum
animals: List(Animal) = [Dog.new(), Cat.new()]
animals.append(bird) # Компилятор: (1) проверяет bird реализует Animal (2) расширяет enumОбработка компилятором:
- Проверить тип возврата параметра
append - Убедиться, что этот тип реализует целевой интерфейс
- Если проходит → расширить enum, разрешить помещение
- Если не проходит → ошибка компиляции
6.4 Диспетчеризация во время выполнения
Последовательность вызова (match по enum на этапе компиляции, ImplementationProof уже стёрт):
animals[0].speak()
↓
Сгенерированный компилятором match:
match animals[0] {
AnimalGroup.Dog(d) => d.speak(),
AnimalGroup.Cat(c) => c.speak(),
AnimalGroup.Bird(b) => b.speak(),
}Сравнение с виртуальной таблицей:
| Виртуальная таблица (Rust) | Enum на этапе компиляции (YaoXiang) | |
|---|---|---|
| Способ поиска | Указатель на виртуальную таблицу → указатель на метод | Enum match → прямой вызов |
| Накладные расходы во время выполнения | Одно косвенное обращение | Сравнение строк/branch (может быть оптимизировано CPU-предсказателем) |
| Генерация на этапе компиляции | Виртуальная таблица | Enum + match |
| Аннотации пользователя | Требуются dyn Trait + 'a | Не требуются |
| ImplementationProof | Не применимо | Стирается на этапе компиляции, не существует во время выполнения |
Преимущества YaoXiang:
- Не требуется аннотация бренда
- Типовая безопасность на этапе компиляции
- Прозрачность для пользователя (не нужно писать
dyn Animal) - ImplementationProof — чистое понятие этапа компиляции, нулевые накладные расходы во время выполнения
6.5 Ограничения и область применения
Текущий период (одна единица компиляции): Полная поддержка. Отслеживание принадлежности охватывает все точки append/конструирования, enum строится инкрементно.
Кросс-единицы компиляции: На этапе линковки объединяются наборы вариантов enum из разных единиц. Дизайн использует тот же механизм, что и линковочный мономорфизм (единицы генерируют частичные enum, линковщик объединяет).
Не поддерживается: Динамические типы во время выполнения (полный утиный тип). Набор типов полностью известен на этапе компиляции.
Анализ вариантов использования
Базовая реализация интерфейса
# Определение интерфейса
Animal: Type = {
speak: (Self) -> String,
}
# Определение типа
Dog: Type = {
x: Int = 10,
Animal,
speak: (Self) -> String = "Woof",
}
# Использование
dog = Dog.new()
dog.speak() # "Woof"Реализация множественных интерфейсов
# Несколько интерфейсов
Animal: Type = {
speak: (Self) -> String,
}
Pet: Type = {
name: (Self) -> String,
}
# Тип реализует несколько интерфейсов
Dog: Type = {
x: Int = 10,
Animal,
Pet,
speak: (Self) -> String = "Woof",
name: (Self) -> String = "Buddy",
}
# Использование
dog = Dog.new()
dog.speak() # "Woof"
dog.name() # "Buddy"Обобщённые интерфейсы
# Обобщённый интерфейс
Container: (T: Type) -> Type = {
add: (self: &mut Self, item: T) -> Void,
get: (self: &Self, index: Int) -> T,
}
# Реализация обобщённого интерфейса
IntList: Type = {
data: Array(Int),
Container(Int),
add: (self: &mut Self, item: Int) -> Void = ...,
get: (self: &Self, index: Int) -> Int = ...,
}Гетерогенные контейнеры
# Определение интерфейса
Animal: Type = {
speak: (Self) -> String,
}
# Определение типа
Dog: Type = {
x: Int,
Animal,
speak: (Self) -> String = "Woof",
}
Cat: Type = {
y: Int,
Animal,
speak: (Self) -> String = "Meow",
}
# Гетерогенный контейнер
animals: List(Animal) = [Dog.new(), Cat.new()]
# Использование
for animal in animals {
print(animal.speak())
}
# Вывод:
# Woof
# MeowСистема плагинов
# Определение интерфейса
Plugin: Type = {
name: (Self) -> String,
execute: (Self) -> Void,
}
# Главная программа
main: () -> Void = {
# Загрузка плагинов
plugin1 = load_plugin("plugin1.so")
plugin2 = load_plugin("plugin2.so")
# Компилятор проверяет: plugin1 и plugin2 должны реализовывать интерфейс Plugin
plugins: List(Plugin) = [plugin1, plugin2]
# Выполнение всех плагинов
for plugin in plugins {
print(plugin.name())
plugin.execute()
}
}Компромиссы
Преимущества
- Простота: не требуется ключевое слово
impl - Гибкость: реализация методов поддерживается внутри или снаружи
- Единство: правила перегрузки согласованы
- Удобство: лаконичный синтаксис значений по умолчанию
- Нулевые накладные расходы: без виртуальной таблицы, сбор типов на этапе компиляции
- Типовая безопасность: сопоставление интерфейса — проверка на этапе компиляции
- Прозрачность для пользователя: не нужно писать
dyn Animal + 'a
Недостатки
- Ограничение: не поддерживаются динамические типы во время выполнения (полный утиный тип)
- Накладные расходы этапа компиляции: требуется генерация вариантов enum и кода диспетчеризации match для каждого интерфейса
- Набор типов: должен быть полностью известен на этапе компиляции (внутри одной единицы компиляции)
Меры смягчения
- Система плагинов: поддержка через проверку сопоставления интерфейса на этапе компиляции
- Набор типов: отслеживание принадлежности, инкрементное построение — сбор ведётся на каждой точке
append/конструирования, а не глобальное сканирование - Кросс-единицы компиляции: на этапе линковки объединяются наборы вариантов enum, используется тот же механизм, что и для линковочного мономорфизма
Альтернативные решения
| Решение | Почему не выбрано |
|---|---|
Ключевое слово impl | Увеличивает синтаксическую сложность |
Виртуальная таблица (dyn Trait) | Требует аннотации бренда ('a) |
| Полный утиный тип | Накладные расходы во время выполнения, отсутствие типовой безопасности |
| Ручная enum-обёртка | Большая нагрузка на пользователя |
Связь с RFC-009
Бренд и реализация интерфейса:
- Реализация интерфейса находится на уровне типа, не затрагивает бренд
- Бренд находится на уровне доказательства заимствования (RFC-009a)
- Они ортогональны и не влияют друг на друга
Динамическая диспетчеризация и бренд:
- Динамическая диспетчеризация использует доказательство реализации, не требует аннотации бренда
- Доказательство реализации генерируется на этапе компиляции, во время выполнения отсутствует поиск
- Это позволяет избежать сложности
dyn Trait + 'a
Наследование интерфейсов
Интерфейс может включать другой интерфейс. Новый синтаксис не вводится — используется та же синтаксическая позиция, что и для объявления интерфейса типом:
Animal: Type = {
speak: (Self) -> String,
}
Pet: Type = {
Animal, # Pet наследует Animal — без нового ключевого слова
name: (Self) -> String,
}
# При реализации Dog интерфейса Pet должны выполняться все методы Animal и Pet
Dog: Type = {
x: Int,
Pet,
speak: (Self) -> String = "Woof", # Из Animal
name: (Self) -> String = "Buddy", # Из Pet
}Принцип проектирования: Наследование существует, но его чрезмерное использование не поощряется. Основной способ комбинирования — через множественные объявления интерфейсов (Dog: Type = { Animal, Pet, ... }). Тип может напрямую объявить все интерфейсы, которые он удовлетворяет, без выражения через дерево наследования. Наследование интерфейсов используется только при наличии чёткой иерархии "is-a".
Обработка компилятором: Раскрытие цепочки наследования. Pet раскрывается в { все методы Animal, name: ... }. Когда Dog объявляет Pet, компилятор проверяет, что Dog удовлетворяет всем методам Animal и Pet.
Реализация методов по умолчанию
Интерфейс может предоставлять реализацию методов по умолчанию. Реализующий тип может выбрать перекрытие или наследование реализации по умолчанию:
fmt: Type = {
display: (Self) -> String, # Обязательно реализовать
debug: (Self) -> String = Self.display(), # ✅ Ссылка на метод того же интерфейса
summary: (Self) -> String = f"<{Self.name}>", # ❌ Ошибка компиляции: Self.name отсутствует в fmt
}Основное ограничение: интерфейс не может предполагать реализацию вышестоящего. Методы по умолчанию могут ссылаться только на методы, объявленные в том же интерфейсе. Поля конкретного типа или методы других интерфейсов невидимы для методов по умолчанию — интерфейс является замкнутым контрактом и не может заглядывать в карманы реализующего типа. Нарушение этого ограничения вызывает ошибку непосредственно при определении интерфейса.
Наследование может предполагать реализацию нижестоящего: Когда интерфейс Pet наследует Animal, методы по умолчанию Pet могут использовать методы, объявленные в Animal — потому что они унаследованы, а значит гарантированно есть.
Animal: Type = {
speak: (Self) -> String,
}
Pet: Type = {
Animal, # Наследование
name: (Self) -> String,
introduce: (Self) -> String = Self.name() + " says " + Self.speak(), # ✅ speak из унаследованного Animal
}Поведение на этапе компиляции: При реализации типом интерфейса, для каждого метода:
- Тип предоставляет → используется метод типа
- Тип не предоставляет, в интерфейсе есть реализация по умолчанию → компилятор встраивает реализацию по умолчанию в тип (нулевые накладные расходы виртуальной таблицы)
- Тип не предоставляет, в интерфейсе нет реализации по умолчанию → ошибка компиляции
Принцип проектирования: Методы по умолчанию аналогичны автоматическому выводу Copy/Clone — компилятор генерирует автоматически при необходимости, пользователь может перекрыть. Не вводятся ключевые слова virtual/override/super.
Этапы реализации
| Этап | Содержание | Зависимость |
|---|---|---|
| Phase 1 | Синтаксис объявления интерфейса | RFC-011 |
| Phase 2 | Внутренние/внешние объявления методов | Phase 1 |
| Phase 3 | Правила перегрузки и перекрытия | Phase 2 |
| Phase 4 | Синтаксис значений по умолчанию | Phase 2 |
| Phase 5 | Наследование интерфейсов | Phase 3 |
| Phase 6 | Реализация методов по умолчанию | Phase 5 |
| Phase 7 | Генерация доказательства реализации | Phase 6 |
| Phase 8 | Сбор типов на этапе компиляции | Phase 7 |
| Phase 9 | Реализация динамической диспетчеризации | Phase 8 |
Запись решений по дизайну
| Решение | Решение | Причина | Дата |
|---|---|---|---|
| Синтаксис объявления интерфейса | Имя интерфейса записывается напрямую в теле типа | Устранение ключевого слова impl, объявление интерфейса — естественная часть определения типа | 2026-06-14 |
| Динамическая диспетчеризация | Сбор типов на этапе компиляции + автоматическая генерация enum | Без виртуальной таблицы, нулевой поиск во время выполнения, прозрачность для пользователя | 2026-06-14 |
| Внешние объявления методов | Поддерживаются | Эквивалентная гибкость с внутренними объявлениями, компилятор отвечает за межфайловый сбор | 2026-06-14 |
| Перекрытие | Запрещено (одинаковые сигнатуры вызывают ошибку) | Перекрытие приводит к непредсказуемому поведению, перегрузка покрывает все случаи | 2026-06-14 |
| Наследование интерфейсов | Поддерживается, без нового синтаксиса | Та же синтаксическая позиция, что для объявления интерфейса типом. Поощряется композиция (множественные объявления интерфейсов), не поощряются глубокие деревья наследования | 2026-07-03 |
| Реализация методов по умолчанию | Поддерживается, аналогично автоматическому выводу Copy/Clone | Интерфейс предоставляет тело, компилятор встраивает в реализующий тип при необходимости; пользователь может перекрыть. Не вводятся virtual/override | 2026-07-03 |
| Ограничение методов по умолчанию | Проверка при определении интерфейса: можно ссылаться только на методы того же интерфейса, нельзя предполагать реализацию вышестоящего | Интерфейс — замкнутый контракт. Наследование может предполагать реализацию нижестоящего, но интерфейс не может предполагать поля/методы реализующего типа | 2026-07-03 |
| Стратегия сбора типов | Отслеживание принадлежности, инкрементное построение — сбор на каждой точке append/конструирования | Не глобальное сканирование всех реализаторов, а инкрементное расширение enum в точках владения | 2026-07-03 |
| ImplementationProof | Чистое понятие этапа компиляции, стирается во время выполнения | Во время выполнения используется enum match для диспетчеризации, доказательство используется только для проверки на этапе компиляции | 2026-07-03 |
| Кросс-единицы компиляции | На этапе линковки объединяются варианты enum из разных единиц | Используется тот же механизм, что и для линковочного мономорфизма, единицы генерируют частичные enum, линковщик объединяет | 2026-07-03 |
Открытые вопросы
- [x]
Наследование интерфейсов (интерфейс может наследовать другие интерфейсы)→ Поддерживается, без нового синтаксиса.Pet: Type = { Animal, ... } - [x]
Реализация методов по умолчанию (интерфейс может предоставлять реализацию по умолчанию)→ Поддерживается, аналогично автоматическому выводу Copy. Интерфейс предоставляет тело, компилятор встраивает при необходимости - [ ] Продвинутое использование ограничений интерфейса (ассоциированные типы, GAT)
- [ ] Взаимодействие с замыканиями (замыкания реализуют интерфейсы)
Список литературы
- RFC-011: Проектирование системы обобщённых типов — родительский RFC
- RFC-009: Проектирование модели владения — система владения
- RFC-009a: Конвейер доказательств заимствования — механизм брендов
- RFC-010: Унифицированный синтаксис типов — унифицированный синтаксис
Жизненный цикл и судьба
| Статус | Расположение | Описание |
|---|---|---|
| На рассмотрении | docs/design/rfc/review/ | Открыто для обсуждения сообщества |
