# 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 的引用计数实现。[官方说明](https://docs.python.org/3/library/copy.html) - **JS:对象赋值保留同一对象;TS 沿用这一运行时行为。** `const a = {n: 1}; const b = a; b.n = 2` 后,`a.n` 为 `2`。`const` 限制重新赋值,不会冻结对象;数字等原始值则没有可共享修改的内部状态。TS 的类型标注不会让赋值自动复制对象。好处是对象共享直接,代价是静态类型本身不消除别名影响。启发是:绑定是否可改、值是否可变、是否共享,需要分别规定。[JS 规范](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-assignment-operators-runtime-semantics-evaluation)、[TS 文档](https://www.typescriptlang.org/docs/handbook/2/basic-types.html#erased-types) - **Go:复制赋给变量的值,但不递归复制它引用的数据。** `a := [1]int{1}; b := a; b[0] = 2` 后,`a[0]` 仍为 `1`;换成切片 `a := []int{1}`,同样操作后 `a[0]` 为 `2`,因为复制的切片描述信息仍引用同一底层数组。好处是支持直接的数据复制,也能方便共享;代价是必须理解类型内部是否含引用。启发是:**“赋值复制值”不保证整份数据相互独立。** [Go 规范](https://go.dev/ref/spec#Assignment_statements) - **Rust:赋值根据类型发生复制或移动。** `let a = 1; let b = a;` 后仍可读 `a`,因为整数实现了 `Copy`;`let a = String::from("hi"); let b = a;` 后再读取 `a` 会被编译器拒绝。好处是无需隐式复制字符串,也能明确资源归属;代价是使用者需要理解移动、借用和复制能力。启发是:按类型区分可行,但需要类型系统支撑;“放在栈上还是堆上”并非判断复制或移动的规则。[Rust 官方教程](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html) 回到我们的待选方案: | 方案 | 普通可变数据的 `b = a` | |---|---| | **A:默认独立值** | 修改 `b` 不影响 `a`;需要明确独立性覆盖到哪一层。 | | **B:默认共享** | 修改共享内容会影响 `a`;需要独立副本时显式复制。 | | **C:默认移动** | 转移使用资格;之后不能继续读取原来的 `a`。 | | **D:按类别区分** | 为普通数据、共享引用、唯一资源分别规定行为;类别必须可识别。 | **我的建议是先从 A 或 B 确定普通可变数据的使用体验**,再在复制、移动任务中检查其他类别是否需要不同规则。这样能先明确用户可观察的行为,不必提前决定完整类型系统或回收机制。这只是推进建议,尚未替你选择。 对于普通可变记录或序列,你更希望默认 **A:各自独立**,还是 **B:共享修改**?如果你希望所有权成为语言的基础,也可以直接选 C 或 D。 ## 讨论来源 《语言设计 04:存储与生命周期》,整理日期 2026-10-28。章节编号沿用原清单。