RFC-009: 所有権モデル設計
摘要
本文档定义 YaoXiang 编程语言的所有权模型(Ownership Model)。
核心设计——五个概念,一个梯度:
看一眼/原地改 拿走 共享持有 复制一份 系统级
│ │ │ │ │
&T Move ref clone() unsafe
&mut T 零拷贝 编译器自动 显式深拷贝 *T
零大小令牌 默认 选Rc/Arc 用户负责
类型属性自然
推导权限- 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)spawn { } 不是函数值。 spawn 标记的 block 和 if/while body 一样——立刻执行,父栈帧存活期间完成。spawn body 可以正常访问外层变量。
跨任务——令牌不能穿线:
# ❌ 令牌不能跨任务边界
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 はコンパイル時一意の整数ブランドの用途:
- 防伪造:令牌只能从所有者胶囊获得,不能凭空构造
- 关联追踪:从
&Point派生&Float(字段访问)时,&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.rc.Weak
# 高级用户显式选择
a.next = ref b
b.prev = Weak.new(a.next) # ユーザー显式控制哪个方向是弱的Weak 不是语言内置,是标准库类型。 日常用 ref 就够了。需要精细控制内存的高级用户手动引入 Weak。
3.5 借用令牌 vs 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 跨任务 → Arc 独立副本
零大小编译后消失 T -> Void 消费 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(明示的なディープコピー、独立コピー作成)とは3段階の梯度を形成する。
Dup 与 Clone 是正交的概念——Dup 复制句柄共享数据,Clone 创建独立副本。一个类型可以同时支持 Dup 和 Clone,也可以只支持其中一个。
| 类型 | Dup | Clone | 说明 |
|---|---|---|---|
&T | ✅(复制令牌,多个视角指向同一数据) | ✅ | 只读令牌 |
ref T | ✅(引用计数+1,共享堆数据) | ✅ | 共享持有(编译器自动选 Rc/Arc) |
| String, Bytes | ✅(内部引用计数,复制句柄共享底层 buffer) | ✅ | 字符串/字节 |
&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是普通类型,不是特殊语言特性。与 RFC-010 のname: type = value完全一致 - 简单:无生命周期,借用检查降维为类型系统命题。
&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 は明示的なパラメータのみ使用、外層変数を暗黙的に捕獲しない | 明示的な哲学、コンパイラを簡略化 | 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、命題を生成して検査を実行するのではなく、命題を生成して証明パイプラインに送り込む。型チェッカーが型等式命題を生成するように、借用命題生成器は借用命題を生成し、同じパイプラインで検証する。
品牌 ID(#42)就是 'a。 信息完全一样,编码不同。'a 在类型签名中可见,#42 在编译器内部。没有发明新分析——把生命周期从类型层降到了证明层。
算法概要(详见 RFC-009a):
- 品牌树前缀匹配 → 确定冲突令牌(O(depth),深度 ≤ 3)
- 反向 BFS → 从消费者出发,break 切断回边,结构分析覆盖 95%+ 场景(快速通道)
- SMT 論理切断 → while とパス条件がある場合にのみ呼び出す(低速パス、極めて稀)
参考文献
YaoXiang 公式ドキュメント
外部参考
ライフサイクルと帰属
| 状態 | 位置 | 说明 |
|---|---|---|
| 草案 | docs/design/rfc/ | 作者草稿、レビュー待ち |
| レビュー中 | docs/design/rfc/ | コミュニティ議論開放 |
| 承認済み | docs/design/accepted/ | 正式設計ドキュメント |
| 拒否 | docs/design/rfc/ | RFC ディレクトリに保存 |
