7.1 KiB
04 · 存储与生命周期
0/20 项已确认。当前推进 04-01;本分类尚未完成选择。
本页整理原讨论的问题、验收要求和候选方案。勾选代表讨论中已确认的规则,不代表编译器或运行时已经实现。
04-01 赋值的默认语义
- 待讨论
明确赋值默认是独立值、共享还是移动;是否按值的类别区分,以及如何识别这种区别。
04-02 创建、身份与存储位置
- 待讨论
明确何时产生新实例、身份是否可观察,以及存储位置和地址是否属于语言保证。依赖:对象与类型模型。
04-03 初始化流程
- 待讨论
明确初始化顺序、必需字段、默认值和“可正常使用”的判定条件。
04-04 初始化期间的访问与逃逸
- 待讨论
明确未完成初始化的值能否被读取、调用、共享或捕获,以及违规行为。
04-05 初始化失败
- 待讨论
明确失败如何传播、已初始化部分如何清理,以及外部可观察状态的保证。
04-06 别名与修改权限
- 待讨论
明确多个引用能否同时存在、谁能修改,以及修改对其他持有者是否可见。
04-07 复制规则
- 待讨论
明确哪些值可复制、浅复制与深复制的含义、内部共享关系及复制失败行为。
04-08 移动规则
- 待讨论
明确是否支持移动、何时发生、源值之后能做什么,以及禁止移动的情况。
04-09 跨函数与闭包传递
- 待讨论
明确传参、返回值、闭包捕获沿用哪些生命周期规则,局部值逃逸如何处理。依赖:函数与闭包相关规则。
04-10 所有权与借用
- 待讨论
明确是否存在所有者、借用及其期限;哪些约束由静态检查或运行时检查保证。依赖:类型系统。
04-11 引用失效与悬空访问
- 待讨论
明确销毁、容器变化、存储迁移等情况下引用何时失效,以及失效访问的处理。
04-12 自动回收机制
- 待讨论
明确采用作用域释放、引用计数、追踪式 GC 或混合机制,并区分语言保证与实现自由。
04-13 循环引用与弱引用
- 待讨论
明确循环如何处理,是否提供弱引用,以及弱引用失效后的访问结果。
04-14 销毁时机与顺序
- 待讨论
明确销毁是否及时、顺序是否保证,以及正常退出和提前退出时的行为。
04-15 清理中的异常行为
- 待讨论
明确清理失败、重复清理、清理期间重入,以及重新发布待销毁值的处理规则。
04-16 非内存资源
- 待讨论
明确文件、锁等资源如何及时释放,是否支持显式关闭或作用域管理,以及关闭后操作的结果。
04-17 用户自定义生命周期操作
- 待讨论
明确哪些创建、复制、移动、清理操作允许定制,其触发时机与限制。依赖:定制协议。
04-18 分配失败与手动存储能力
- 待讨论
明确内存不足如何表现,以及是否暴露手动分配、释放或分配器选择;不支持也须明确。
04-19 并发、异步与外部接口边界
- 待讨论
明确跨执行单元共享或转移、任务取消、外部代码持有值时的生命周期契约;不支持的能力明确排除。依赖:相应并发及外部接口规则。
04-20 跨分类一致性验收
- 待讨论
用创建、共享、传递、失败、退出的完整例子检查已定规则;解决与类型、对象、协议和运行时规则的冲突。
当前问题的方案与语言对照
以下沿用原讨论材料;建议和参考语言的做法均不自动成为本语言规则。
当前仍是 04-01:b = a 默认意味着什么? 重点看两个问题:修改 b 是否影响 a,赋值后还能否使用 a。
-
Python:赋值建立绑定,不复制对象。
a = [1]; b = a; b.append(2)后,a也是[1, 2]。但给b重新赋值不会改变a的绑定。共享很方便,代价是可变对象的修改会沿别名传播。对我们的启发是:必须分清“修改同一个值”和“变量改为指向另一个值”。这是 Python 的语义,不依赖 CPython 的引用计数实现。官方说明 -
JS:对象赋值保留同一对象;TS 沿用这一运行时行为。
const a = {n: 1}; const b = a; b.n = 2后,a.n为2。const限制重新赋值,不会冻结对象;数字等原始值则没有可共享修改的内部状态。TS 的类型标注不会让赋值自动复制对象。好处是对象共享直接,代价是静态类型本身不消除别名影响。启发是:绑定是否可改、值是否可变、是否共享,需要分别规定。JS 规范、TS 文档 -
Go:复制赋给变量的值,但不递归复制它引用的数据。
a := [1]int{1}; b := a; b[0] = 2后,a[0]仍为1;换成切片a := []int{1},同样操作后a[0]为2,因为复制的切片描述信息仍引用同一底层数组。好处是支持直接的数据复制,也能方便共享;代价是必须理解类型内部是否含引用。启发是:“赋值复制值”不保证整份数据相互独立。 Go 规范 -
Rust:赋值根据类型发生复制或移动。
let a = 1; let b = a;后仍可读a,因为整数实现了Copy;let a = String::from("hi"); let b = a;后再读取a会被编译器拒绝。好处是无需隐式复制字符串,也能明确资源归属;代价是使用者需要理解移动、借用和复制能力。启发是:按类型区分可行,但需要类型系统支撑;“放在栈上还是堆上”并非判断复制或移动的规则。Rust 官方教程
回到我们的待选方案:
| 方案 | 普通可变数据的 b = a |
|---|---|
| A:默认独立值 | 修改 b 不影响 a;需要明确独立性覆盖到哪一层。 |
| B:默认共享 | 修改共享内容会影响 a;需要独立副本时显式复制。 |
| C:默认移动 | 转移使用资格;之后不能继续读取原来的 a。 |
| D:按类别区分 | 为普通数据、共享引用、唯一资源分别规定行为;类别必须可识别。 |
我的建议是先从 A 或 B 确定普通可变数据的使用体验,再在复制、移动任务中检查其他类别是否需要不同规则。这样能先明确用户可观察的行为,不必提前决定完整类型系统或回收机制。这只是推进建议,尚未替你选择。
对于普通可变记录或序列,你更希望默认 A:各自独立,还是 B:共享修改?如果你希望所有权成为语言的基础,也可以直接选 C 或 D。
讨论来源
《语言设计 04:存储与生命周期》,整理日期 2026-10-28。章节编号沿用原清单。