RFC-009: Модель владения
Резюме
Этот документ определяет модель владения (Ownership Model) языка программирования YaoXiang.
Ключевая идея — пять концепций, один градиент:
Взглянуть/изменить Забрать Совместное Скопировать Системный
на месте владение копию уровень
│ │ │ │ │
&T Move ref clone() unsafe
&mut T Без копирования Rc/Arc *T
Токен по выбору
нулевого компилятора
размера
Свойства типа
естественно
выводят
права- Move (по умолчанию): присваивание / передача параметра / возврат = передача владения, без копирования, автоматическое освобождение через RAII
&T/&mut T(токены заимствования): токены нулевого размера на этапе компиляции.&Tкопируемый (разделяемое только чтение),&mut Tлинеен (исключительная мутация). Права естественно выводятся из свойств типа, без специальных правил. Можно возвращать, можно хранить в структурах.ref(ключевое слово): совместное владение между областями видимости. Компилятор автоматически выбирает Rc (без пересечения задач) или Arc (с пересечением задач)clone(): явное глубокое копированиеunsafe+*T: сырые указатели, системный аварийный люк
Устранённая сложность:
- ❌ Нет времён жизни
'a - ❌ Нет отдельного фреймворка проверки заимствований (конфликты заимствований сводятся к утверждениям Хоара, разделяют конвейер доказательств с проверкой типов)
- ❌ Нет GC
- ❌ Нет специальных правил вроде «запрета на побег» (токен — обычный тип, область видимости обрабатывается системой типов единообразно)
- ❌ Пользователю не нужно знать разницу между Rc и Arc (компилятор выбирает автоматически)
Бремя программиста:
&Tкопируем,&mut Tнекопируем — два свойства типа, ноль специальных правил, компилятор всё делает автоматически. Гарантии производительности: Move без накладных расходов, токены без накладных расходов (типы нулевого размера, исчезают после компиляции), ref оплачивается по мере надобности, без пауз GC.
Мотивация
Зачем нужна модель владения?
| Язык | Управление памятью | Проблема |
|---|---|---|
| C/C++ | Ручное управление | Утечки памяти, висячие указатели, двойное освобождение |
| Java/Python | GC | Колебания задержки, накладные расходы памяти, непредсказуемые паузы |
| Rust | Владение + проверка заимствований | Крутая кривая обучения из-за времён жизни 'a |
| YaoXiang | Move + Token + ref | Просто, детерминированно, без GC |
Цели проектирования
# 1. 默认 Move(零拷贝)
p = Point(1.0, 2.0)
p2 = p # Move,p 不可再读
# 2. &T / &mut T 借用令牌(零开销,类型属性自然推导权限)
print_info(p2) # 编译器自动创建 &Point 令牌,用完即释放
shift(p2, 1.0, 1.0) # 编译器自动创建 &mut Point 令牌
# 3. ref = 共享(编译器自动选 Rc/Arc)
shared = ref p2 # 跨作用域持有
spawn { use(shared) } # 编译器:跨任务 → Arc
# 4. clone() = 显式复制
backup = p2.clone() # 深拷贝,独有
# 5. unsafe + *T = 系统级
unsafe {
ptr: *Point = &p
(*ptr).x = 0.0
}Ключевые отличия от Rust
| Свойство | Rust | YaoXiang |
|---|---|---|
| Семантика по умолчанию | Заимствование &T (нужен явный .clone()) | Move (передача значения, без копирования) |
| Заимствование | &T/&mut T, можно возвращать, нужны времена жизни | &T/&mut T — токены нулевого размера, свойства типа Dup/Linear естественно выводят права |
| Механизм совместного владения | Arc::new() + ручной Weak | ref — ключевое слово (компилятор автоматически выбирает Rc/Arc) |
| Копирование | clone() | clone() |
| Сырые указатели | *T | *T |
| Времена жизни | 'a | ❌ Нет |
| Проверка заимствований | Глобальный вывод | Проверщик типов автоматически генерирует предикаты заимствования, единый конвейер доказательств |
| Циклические ссылки | Ручной Weak | Единое освобождение по завершении задачи / lint для межзадачных / Weak из стандартной библиотеки |
Предложение
1. Move (передача владения по умолчанию)
# 规则:赋值 / 传参 / 返回 = Move,零拷贝
p: Point = Point(1.0, 2.0)
p2 = p # Move,p 不可再读
# 变量可以重新赋值(Python 风格,无遮蔽)
p = Point(3.0, 4.0) # p 重新绑定,类型必须一致
# 函数参数:Move
process: (p: Point) -> Point = {
p.transform()
p # Move 返回
}
# 函数返回:Move
create: () -> Point = {
p = Point(1.0, 2.0)
p # Move 返回,零拷贝
}Особенности:
- Без копирования (компилятор перемещает указатель)
- После перемещения исходная привязка недоступна для чтения (ошибка компиляции)
- RAII: автоматическое освобождение при выходе из области видимости
- Сигнатура функции
(T) -> Tсама по себе является документацией — потребляет T, возвращает T
2. &T / &mut T (токены заимствования)
Ключевой принцип: &T и &mut T являются токенами нулевого размера на этапе компиляции. Это не «ссылки», а «доказательства уровня типа для права доступа».
2.1 Два свойства типа
&T → Нулевой размер, замораживает исходные данные (пока жив ReadToken, WriteToken запрещён),
под гарантией замораживания несколько представлений только для чтения безопасны → копируем (Dup)
&mut T → Нулевой размер, исключительные чтение-запись (пока жив WriteToken, любой другой токен запрещён),
копирование при исключительном доступе бессмысленно → линеен (не Dup)Причинно-следственная связь не может быть обратной: замораживание — причина, Dup — следствие. Не потому что &T реализует Dup и потому может сосуществовать — а потому что данные заморожены (мутация невозможна), несколько представлений только для чтения безопасны, и Dup может быть реализован. Если принять Dup за определение, а проверку конфликтов за «дополнительную заплатку», проектирование ошибочно.
2.2 Базовое использование
# 方法端:声明参数类型,决定需要的权限
Point.print: (self: &Point) -> Void = {
print(self.x) # &Point 令牌授予读权限
print(self.y)
}
Point.shift: (self: &mut Point, dx: Float, dy: Float) -> Void = {
self.x = self.x + dx # &mut Point 令牌授予写权限
self.y = self.y + dy
}
# 调用端:编译器自动选择借用或 Move
p = Point(1.0, 2.0)
p.print() # 编译器自动创建 &Point 令牌
p.shift(1.0, 1.0) # 编译器自动创建 &mut Point 令牌
p.print() # OK,上一个令牌已随 shift 调用结束而释放
# 自由函数同理
distance: (a: &Point, b: &Point) -> Float = {
sqrt((a.x - b.x)**2 + (a.y - b.y)**2) # 两个 &Point 令牌共存——Dup 类型
}
d = distance(p, p2)2.3 Почему не нужно «запрещать побег»
RFC-009 v8 наложил на &T/&mut T три специальных правила — только как параметр, нельзя возвращать, нельзя хранить в структурах. Это заплатки к концепции «заимствования».
Система токенов не нуждается в этих правилах. Токен — это обычный тип, подчиняющийся тем же правилам области видимости, что и все остальные типы.
Возврат ссылки — естественная поддержка:
# ✅ 令牌随返回值一起传播
Point.get_x: (self: &Point) -> (&Float, &Point) = {
return (&self.x, self) # 子令牌和父令牌一起返回
}
# 使用
p = Point(1.0, 2.0)
(px_ref, p) = p.get_x() # 令牌返回给调用者
print(px_ref) # OK,令牌仍在作用域Хранение в структуре — естественная поддержка:
# ✅ 结构体携带令牌作为字段
Window: Type = {
target: Point,
view: &Point, # 令牌字段——持有对 target 的只读视图
}
# view 的令牌从 target 派生,Window 持有两者的所有权
# 只要 Window 存在,view 令牌就有效2.3 Замыкания и Lambda с явными параметрами
Lambda — это значение функции — его можно возвращать, хранить, передавать за пределы текущей области видимости. Поэтому Lambda не захватывает неявно внешние локальные переменные. Когда нужны внешние данные, они передаются через явные параметры:
# ✅ Lambda 使用显式参数
double: (x: Int) -> Int = (x) => x * 2
filter_by: (items: List(Int), f: (Int) -> Bool) -> List(Int) = { ... }
# ✅ spawn { } 不受此规则影响——spawn 是立刻执行的并发块,父任务阻塞等待
shared = ref data
spawn { use(shared) }
# ❌ Lambda 不能隐式捕获外层变量
x = 42
f = () => { x + 1 } # 编译错误:x 不在作用域内
# ✅ 正确方式:显式传参
f = (x) => { x + 1 }
f(x)
# ✅ 正确方式二:上下文在创建点固化(柯里化)——闭包只吃参数,不捕获
gt: (t: Int) -> (x: Int) -> Bool = (x) => x > t
evens = list.filter(nums, gt(threshold))Дополнение (2026-08-17): правильное решение для зависимости от контекста — фиксация через каррирование, а не захват. После побега замыкания область видимости в точке его определения может уже не существовать, поэтому неявный захват запрещён; но область видимости в точке вызова (точка создания) гарантированно жива, и фиксация контекста в этой точке в виде значения, попадающего в замыкание, безопасна. См. SPEC §12.3.
spawn { } — это не значение функции. Блок, помеченный spawn, как и тело if/while — выполняется немедленно, завершаясь пока жив родительский кадр стека. Тело spawn может нормально обращаться к внешним переменным.
Межзадачное взаимодействие — токены не пересекают границу:
# ❌ 令牌不能跨任务边界
bad_task: (p: &Point) -> Void = {
spawn { print(p.x) } # ❌ 编译错误:令牌不能跨任务传递
}
# 这不是特殊规则——令牌是编译期权限证明,跨任务共享请使用 ref
# 如果需要跨任务共享,请使用 refТокен не может быть ref:
# ❌ 令牌是权限证明,不是所有权
bad_ref: (p: &Point) -> Void = {
shared = ref p # ❌ 编译错误:&T 不是可拥有类型
}2.4 Жизненный цикл токена
Жизненный цикл токена определяется обычными правилами области видимости, без параметров времени жизни:
- Токен в параметрах функции: живёт во время вызова, освобождается после его завершения
- Возвращаемый токен: владение передаётся вызывающему
- Токен, хранящийся в структуре: живёт вместе со структурой
Компилятору не нужна аннотация 'a, поскольку токен — это значение, время жизни которого управляется системой владения (Move / RAII) единообразно. Сведение проблемы заимствования к проблеме владения.
2.5 Обнаружение конфликтов токенов
Обнаружение конфликтов токенов — это утверждение логики Хоара, а не отдельный потокочувствительный анализ.
{Все конфликтующие ReadToken мертвы} write(data) {WriteToken безопасно получен}Разделяет конвейер доказательств из RFC-027 с проверкой типов и верификацией пользовательских предикатов. Компилятор автоматически генерирует предикаты заимствования (borrow_conflict, use_after_move, use_after_drop, mut_violation) и отправляет их в конвейер. Конвейер возвращает Proved / Disproved / Unproven.
# ❌ &mut 令牌是线性的,不能复制
bad_dup: (p: &mut Point) -> Void = {
p2: &mut Point = p # Move,p 不可再读
p.x = 10.0 # ❌ 编译错误:WriteToken 已被移动
}
# ✅ &T 令牌是 Dup 类型,可以自由复制
good_dup: (p: &Point) -> Void = {
p2: &Point = p # OK,&T 是 Dup 类型
print(p.x) # OK
print(p2.x) # OK,两个只读令牌共存
}Проверка заимствований не исчезла — она понижена в размерности. Существующий BorrowChecker становится BorrowPredicateEmitter (генератором предикатов); генерируемые предикаты заимствования разделяют один конвейер доказательств с другими предикатами типов. Это полностью параллельно концепции проверщика типов: проверщик типов генерирует предикаты равенства типов, генератор предикатов заимствования генерирует предикаты заимствования, верификация выполняется одним конвейером. Подробности см. в RFC-009a.
2.7 Внутреннее устройство компилятора: механизм брендинга
Пользователь никогда не сталкивается с брендами. Компилятор внутри назначает каждому токену уникальный на этапе компиляции идентификатор:
Что видит Внутреннее представление
пользователь компилятора
────────────────────────────────────────
&Point → ReadToken(Point, #N) // #N — уникальное целое на этапе компиляции
&mut Point → WriteToken(Point, #M) // #M — уникальное целое на этапе компиляцииНазначение брендов:
- Защита от подделки: токен можно получить только из капсулы владельца, нельзя сконструировать из ниоткуда
- Отслеживание связей: при порождении
&Floatиз&Point(доступ к полю)&Floatнесёт порождённый бренд (#N.field_x), компилятор может отследить его до родительского токена - Обнаружение конфликтов: одноимённые
WriteTokenи порождённыеReadTokenне могут быть одновременно активны
Бренды полностью исчезают после мономорфизации и встраивания, в генерируемом машинном коде их нет. Нулевые накладные расходы во время выполнения.
2.8 Правила автоматического выбора заимствования
На стороне вызова компилятор автоматически выбирает по следующему приоритету:
1. Если аргумент далее используется → приоритетно создаётся токен (&T или &mut T, согласно сигнатуре метода)
2. Если аргумент далее не используется → Move
3. Порядок приоритета совпадения: &T < &mut T < Move# 示例:自动选择
p = Point(1.0, 2.0)
p.print() # print 声明 &self → 编译器创建 &Point 令牌
p.shift(1.0, 1.0) # shift 声明 &mut self → 编译器创建 &mut Point 令牌
p2 = p # Move,p 不再使用2.9 Сравнение с упрощённым заимствованием в RFC-009 v8
| Свойство | Упрощённое заимствование (v8) | Токен заимствования (v9) |
|---|---|---|
| Возврат ссылки | ❌ Жёстко запрещено | ✅ Токен распространяется с возвращаемым значением |
| Хранение в структуре | ❌ Жёстко запрещено | ✅ Токен как поле структуры |
| Явные параметры Lambda | ❌ Жёстко запрещено | ✅ Lambda использует явные параметры |
| Специальные правила | 3 (только параметр / нельзя возвращать / нельзя хранить) | 0 — свойства типа естественно выводят права |
| Проверка заимствований | Специализированная перекрёстная проверка | Потокочувствительный анализ активности в проверщике типов |
| Аннотации времён жизни | Не нужны | Не нужны |
| Накладные расходы во время выполнения | Нулевые | Нулевые (тип нулевого размера, исчезает после компиляции) |
| Сообщения об ошибках | «Заимствование не может убежать» | «WriteToken(#3) уже перемещён» (обычная ошибка типа) |
| Ментальная модель пользователя | Понимание «особого статуса» заимствования | &T копируем, &mut T некопируем |
3. Ключевое слово ref (автоматическая оптимизация компилятора)
ref — единственный способ совместного владения между областями видимости. Внутри это Rc или Arc, пользователю не нужно об этом заботиться.
3.1 Базовое использование
p: Point = Point(1.0, 2.0)
shared = ref p # 共享,编译器自动选实现
# 跨任务共享
@block
main: () -> Void = {
data = ref heavy_data
spawn { use(data) } # 编译器:跨任务 → Arc
spawn { use(data) } # 编译器:跨任务 → Arc
}
# 单任务共享
@block
main: () -> Void = {
data = ref heavy_data
use(data) # 编译器:不跨任务 → Rc
}Ментальная модель пользователя: ref = совместное владение. Достаточно.
3.2 Анализ побегов в компиляторе: Rc vs Arc
Анализ потока данных ref:
Не убегает в другие задачи → Rc (неатомарный подсчёт ссылок, низкие накладные расходы)
Убегает в другие задачи → Arc (атомарный подсчёт ссылок, потокобезопасный)3.3 Стратегия обнаружения циклов
Внутризадачные циклы → молчаливое разрешение.
├── Каждая задача имеет чёткие границы жизненного цикла — при завершении задачи все ресурсы (включая циклы ref) единообразно освобождаются.
├── Долгоживущие сервисы должны создавать подзадачи на запрос/соединение — подзадачи автоматически освобождаются, накопления утечек не происходит.
├── ref всегда поддерживает жизнь, семантика не разбавляется.
└── Пользователь имеет право строить двусторонние сильные ссылки внутри задачи (например, промежуточные состояния графовых вычислений).
Межзадачные циклы → lint (по умолчанию warn, настраивается).
├── Поведение программы корректно, реальной утечки нет (при завершении родительской задачи все ресурсы подзадач освобождаются).
├── Но межзадачные сильные ссылки означают размытие границ владения, что заслуживает переосмысления.
├── По умолчанию уровень warn, компиляция проходит с предупреждением.
└── Команда может установить deny в конфигурации проекта, включив в шлюз качества CI.Уровни lint (аналог Rust clippy):
| Уровень | Поведение | Сценарий |
|---|---|---|
allow | Без проверки | Личный проект |
warn (по умолчанию) | Компиляция проходит, есть предупреждение | Этап разработки |
deny | Сбой компиляции | Командный шлюз качества CI |
forbid | Сбой компиляции, нельзя переопределить | Организационное обязательное правило |
# 任务内环:静默允许,双向强引用
build_graph: () -> Void = {
a = Node("a")
b = Node("b")
a.next = ref b
b.prev = ref a # 环。任务结束时统一释放。
}
# 跨任务环:lint(默认 warn)
@block
parent_task: () -> Void = {
shared_a = ref a
shared_b = ref b
spawn {
shared_a.child = ref shared_b # ⚠️ warn: 跨任务循环引用
}
}Пример конфигурации проекта:
# yaoxiang.toml
[lints]
cross-task-cycle = "deny" # 跨任务环在 CI 上直接拒绝| Тип цикла | Поведение | Причина |
|---|---|---|
| Внутризадачный цикл ref | Без проверки | Право пользователя, единообразное освобождение по завершении задачи |
| Межзадачный цикл ref | lint (по умолчанию warn) | Предупреждение о необходимости переосмысления, настраивается на deny |
3.4 Weak: предоставляется стандартной библиотекой
use std.weak
# 高级用户显式选择
a.next = ref b
b.prev = std.weak.new(a.next) # 用户显式控制哪个方向是弱的Weak не встроен в язык, это тип из стандартной библиотеки. Для повседневного использования достаточно ref. Продвинутые пользователи, которым нужен тонкий контроль памяти, вручную подключают Weak.
Редакция 2026-08-03: реализовано как отдельный модуль
std.weak(std.rcне существует —ref— ключевое слово языка, а не модуль; путь модуля унифицирован какstd.weak, точки входа конструирования / обновления —std.weak.new/std.weak.upgrade). Первоначально задуманный в черновикеstd.rc.Weakне реализован, действует данная редакция.
3.5 Токены заимствования против ref
&T / &mut T | ref | |
|---|---|---|
| Что делает | Взглянуть / изменить на месте | Совместное владение |
| Область | С областью видимости значения токена | Между областями видимости |
| Стоимость | Без накладных расходов (тип нулевого размера) | Rc или Arc (выбирает компилятор) |
| Побег | Возможен (токен распространяется с возвращаемым значением / структурой / замыканием) | Собственно для этого и предназначен |
| Межзадачное использование | Невозможно (токен — доказательство прав на этапе компиляции, не может пересекать границу задач) | Возможно (компилятор автоматически выбирает Arc) |
| Циклы | Не применимо | Внутри задачи молчаливое разрешение, межзадачные — lint |
4. clone() — явное копирование
p: Point = Point(1.0, 2.0)
p2 = p.clone() # 深拷贝
# p 和 p2 独立,互不影响Когда использовать: когда нужно сохранить исходное значение и при этом не подходят ни Move, ни совместное владение.
5. unsafe + сырые указатели (системное программирование)
p: Point = Point(1.0, 2.0)
unsafe {
ptr: *Point = &p # 裸指针
(*ptr).x = 0.0 # 解引用(用户保证安全)
ptr2 = ptr + 1 # 指针运算
}Ограничения:
- Можно использовать только в блоке
unsafe - Пользователь гарантирует отсутствие висячих указателей и использования после освобождения
- Применяется для FFI, операций с памятью и другого системного программирования
6. Обзор градиента владения
Токен заимствования Move Совместное Копирование
(без накладных) (без накладных) (по мере надобности)
│ │ │ │
&T копируемый токен Передача владения ref Rc/Arc clone()
&mut T линейный токен по умолчанию компилятор Явное
Цепное потребление автоматически глубокое
с возвратом выбирает копирование
│ │ │ │
Область видимости Внутри области Между областями В любой
значения токена видимости видимости момент
Можно вернуть/ T -> T возврат ref межзадачный Независимая
хранить в структуре T -> Void → Arc копия
Нулевой размер, потребление ref внутризадачный
исчезает после → Rc
компиляции Внутризадачные
циклы молча
разрешены
Межзадачные циклы
— lint
Weak из стандартной
библиотеки как
аварийный люкКомплексный пример
Point: Type = {
x: Float,
y: Float,
# &T:只读令牌
print: (self: &Point) -> Void = {
print(self.x)
print(self.y)
}
# &mut T:可变令牌
shift: (self: &mut Point, dx: Float, dy: Float) -> Void = {
self.x = self.x + dx
self.y = self.y + dy
}
# Move → Move:消费回流
scale: (self: Point, f: Float) -> Point = {
self.x = self.x * f
self.y = self.y * f
self # 拿走,改,还给你
}
# 返回引用:令牌随返回值传播
get_x: (self: &Point) -> (&Float, &Point) = {
return (&self.x, self)
}
}
# Lambda 显式参数
double: (x: Int) -> Int = (x) => x * 2
# 综合使用
p = Point(1.0, 2.0)
p.print() # &Point 令牌
p.shift(1.0, 1.0) # &mut Point 令牌
p = p.scale(2.0) # Move → 回流
shared = ref p # ref 共享
spawn { use(shared) }
# clone 独立副本
backup = p.clone()
# 任务内环:静默允许
a = Node("a")
b = Node("b")
a.next = ref b
b.prev = ref a # 环,任务结束时统一释放
# unsafe 系统级
unsafe {
ptr: *Point = &p
(*ptr).x = 0.0
}Ограничения системы типов
Свойство типа Dup
Dup (Duplicable) — автоматически управляемое компилятором свойство типа, означающее поверхностное копирование: при присваивании / передаче параметра копируется дескриптор / токен, а базовые данные разделяются. Это образует трёхуровневый градиент с Move (передача владения) и Clone (явное глубокое копирование, создающее независимую копию).
Dup и Clone — ортогональные концепции — Dup копирует дескриптор с разделением данных, Clone создаёт независимую копию. Тип может одновременно поддерживать Dup и Clone, а может только один из них.
| Тип | Dup | Clone | Описание |
|---|---|---|---|
&T | ✅ (копирование токена, несколько представлений указывают на те же данные) | ✅ | Токен только для чтения |
ref T | ✅ (счётчик ссылок +1, разделяет данные в куче) | ✅ | Совместное владение (компилятор автоматически выбирает Rc/Arc) |
| String, Bytes | ✅ (внутренний счётчик ссылок, копирование дескриптора разделяет базовый буфер) | ✅ | Строка / байты |
&mut T | ❌ (линеен, исключительный) | ❌ | Токен мутации |
*T | ❌ | ❌ | Сырой указатель |
| struct | Производное (автоматически, если все поля Dup) | ✅ | Структура |
Примитивные типы значений (Int, Float, Bool, Char) при присваивании подвергаются встроенному в компилятор копированию значения — два значения полностью независимы, это не поверхностное копирование. Они не относятся к свойству типа Dup, а обрабатываются компилятором изначально.
Анализ производительности
| Операция | Стоимость | Описание |
|---|---|---|
| Move | Ноль | Перемещение указателя |
&T / &mut T | Ноль | Тип нулевого размера, исчезает после компиляции, нулевые накладные расходы во время выполнения |
ref (внутри задачи) | Низкая | Компилируется в Rc, неатомарные операции |
ref (межзадачный) | Средняя | Компилируется в Arc, атомарные операции |
clone() | Зависит от типа | Быстро для малых объектов, медленно для больших |
unsafe + *T | Ноль | Прямые операции с памятью |
Сравнение
| Язык | Механизм совместного использования | Управление памятью | Обработка циклов | Сложность |
|---|---|---|---|---|
| Rust | Arc / Mutex + проверка заимствований | Проверка на этапе компиляции | Ручной Weak | Высокая |
| Go | chan / pointer | GC | GC | Низкая |
| C++ | shared_ptr | RAII | weak_ptr | Средняя |
| YaoXiang | ref + токены заимствования | RAII | Освобождение по границе задачи / lint для межзадачных / Weak из стандартной библиотеки | Низкая |
Компромиссы
Достоинства
- Единообразие:
&T/&mut T— обычные типы, не специальные языковые конструкции. Полностью соответствуетname: type = valueиз RFC-010 - Простота: нет времён жизни, проверка заимствований сводится к предикатам системы типов.
&Tкопируем,&mut Tнекопируем — два свойства типа - Мощность: можно возвращать ссылки, хранить в структурах, захватывать замыканиями — выразительность на уровне Rust
- Интеллект компилятора: ref автоматически выбирает Rc / Arc, вызывающая сторона автоматически выбирает заимствование
- Детерминированность: ref — это гарантированная жизнь, без тихого превращения в слабую ссылку
- Высокая производительность: Move без копирования, токены без накладных расходов (тип нулевого размера, исчезает после компиляции)
- Гибкость:
unsafe + *Tподдерживает системное программирование
Недостатки
- Распространение параметров дженериков бренда: токены несут идентификатор бренда, в сигнатурах функций, возвращающих ссылки, появляются дополнительные параметры дженериков
- Накладные расходы ref во время выполнения: атомарные операции имеют стоимость (но это неизбежная цена совместного использования)
- Риск unsafe: пользователь должен гарантировать корректность
- Межзадачные циклы — это lint, а не ошибка компиляции: в отличие от Rust не вызывает сбой компиляции, по умолчанию warn, для использования в качестве шлюза качества требуется настройка deny на уровне команды
Альтернативы
| Вариант | Почему не выбран |
|---|---|
| GC | Накладные расходы во время выполнения, непредсказуемые паузы |
| Проверщик заимствований Rust | Требует времён жизни 'a, крутая кривая обучения |
| Чистый Move | Не справляется с конкурентным совместным использованием |
| Без сырых указателей | Невозможно системное программирование |
| Показывать Rc / Arc пользователю | Вываливает детали реализации на пользователя, увеличивает когнитивную нагрузку |
| Упрощённое заимствование (v8) | Стратегия запрета побега жертвует ключевыми выразительными возможностями: захват замыканиями, возврат ссылок и т. д. |
Запись решений по проектированию
| Решение | Определение | Причина | Дата |
|---|---|---|---|
| Значение по умолчанию | Move (без копирования) | Высокая производительность, без накладных расходов | 2025-01-15 |
| Механизм совместного владения | Ключевое слово ref, автоматическая оптимизация компилятора | Просто для пользователя, компилятор отвечает | 2025-01-15 |
| Заимствование | &T / &mut T как токены нулевого размера | Свойства типа (Dup / Linear) естественно выводят права, единая система типов | 2025-01-15 |
| Токен заимствования | Замена упрощённого заимствования, &T Dup, &mut T Linear | Устранение специальных правил вроде «запрета побега», поддержка захвата замыканиями / возврата ссылок / хранения в структурах | 2026-05-29 |
| Копирование | clone() | Явная семантика | 2025-01-15 |
| Системный уровень | *T + unsafe | Поддержка системного программирования | 2025-01-15 |
| Времена жизни | Не реализуются | Токен — значение, его время жизни управляется Move / RAII единообразно, сведение заимствования к проблеме владения | 2025-01-15 |
| Rc / Arc | Компилятор выбирает автоматически, невидимо для пользователя | Снижение когнитивной нагрузки | 2025-01-15 |
| Циклические ссылки | Внутри задачи без проверки, межзадачные — lint (по умолчанию warn) | Структурированная конкурентность даёт естественную гарантию, lint настраивается на deny | 2025-01-16 |
| Weak | Предоставляется стандартной библиотекой | Явный выбор для продвинутых пользователей | 2025-01-16 |
| Анализ потребления | Удалён | Минимальный проверщик заимствований не нужен | 2026-05-11 |
| Возврат владения | Удалён | Сигнатура (T) -> T сама по себе документация | 2026-05-11 |
| Переиспользование пустого состояния | Удалено (как возможность) | Переприсваивание после Move — естественное поведение | 2026-05-11 |
| Обратные функции / частичное потребление / трёхуровневая мутация полей | Удалены | Избыточное проектирование | 2026-05-11 |
| Lambda без неявного захвата | Lambda использует только явные параметры, не захватывает внешние переменные неявно; контекст фиксируется через каррирование в точке создания (SPEC §12.3) | Область видимости в точке определения замыкания может уже не существовать; фиксированное значение в точке создания (область вызова жива) безопасно | 2026-06-16 |
История версий
| Версия | Основные изменения | Дата |
|---|---|---|
| v1 | Черновик: модель владения на основе Rust | 2025-01-08 |
| v8 | Удалено избыточное проектирование (обратные функции / частичное потребление / трёхуровневая мутация полей / анализ потребления / возврат владения / переиспользование пустого состояния), добавлено упрощённое заимствование &T/&mut T | 2026-05-11 |
| v9 | Система токенов заимствования заменила упрощённое заимствование, унифицирована система типов; обнаружение конфликтов токенов исправлено на утверждения Хоара, см. RFC-009a | 2026-06-13 |
Открытые вопросы
| Вопрос | Описание | Статус |
|---|---|---|
| Синтаксис Drop | Нужна ли явная функция drop() | К обсуждению |
| Алгоритм анализа побегов | Реализация межзадачного обнаружения для ref | К обсуждению |
| Обнаружение конфликтов токенов | Утверждения логики Хоара, см. ниже | ✅ Решено (подробности в RFC-009a) |
Обнаружение конфликтов токенов: утверждения логики Хоара
Полное решение по обнаружению конфликтов токенов см. в RFC-009a: Анализ жизненного цикла токенов — на основе конвейера доказательств Хоара. Ключевые моменты:
Активность токенов — это утверждение логики Хоара.{Все конфликтующие ReadToken мертвы} write(data) {WriteToken безопасно получен} — разделяет конвейер доказательств из RFC-027 с проверкой типов и верификацией пользовательских предикатов. Компилятор автоматически генерирует предикаты заимствования (borrow_conflict, use_after_move, use_after_drop, mut_violation), конвейер возвращает Proved / Disproved / Unproven.
Проверка заимствований не исчезла — она понижена в размерности. BorrowChecker становится BorrowPredicateEmitter, генерирующим предикаты, а не выполняющим проверку. Это полностью параллельно концепции «проверщика типов»: проверщик типов генерирует предикаты равенства типов, генератор предикатов заимствования генерирует предикаты заимствования, верификация выполняется одним конвейером.
Идентификатор бренда (#42) — это и есть 'a. Информация полностью та же, кодирование разное. 'a виден в сигнатурах типов, #42 — внутри компилятора. Никакого нового анализа не изобретено — время жизни перенесено с уровня типов на уровень доказательств.
Схема алгоритма (подробности в RFC-009a):
- Сопоставление префиксов дерева брендов → определение конфликтующих токенов (O(depth), глубина ≤ 3)
- Обратный BFS → от потребителя, разрыв обратных рёбер, структурный анализ покрывает 95 %+ сценариев (быстрый канал)
- Логическое отсечение SMT → только при
while+ условиях пути (медленный канал, крайне редко)
Ссылки
Официальная документация YaoXiang
- Спецификация языка
- Манифест проектирования
- RFC-001 Модель spawn
- RFC-010 Унифицированный синтаксис типов
- tutorial/ учебник
Внешние ссылки
Жизненный цикл и размещение
| Статус | Расположение | Описание |
|---|---|---|
| Черновик | docs/design/rfc/ | Черновик автора, ожидает рецензирования |
| На рецензии | docs/design/rfc/ | Открыто для обсуждения сообщества и обратной связи |
| Принято | docs/design/accepted/ | Становится официальным документом проектирования |
| Отклонено | docs/design/rfc/ | Сохраняется в каталоге RFC |
