Skip to content

运行时与内存模型

目标

Qlang 的内存模型必须同时满足三件事:

  1. 大多数开发者不需要显式写生命周期
  2. 生成的代码仍然接近系统级性能
  3. FFI 边界上的所有权关系足够清晰

这决定了它不能简单照搬 GC,也不能把完整 borrow checker 语法直接推给用户。

总体方案

Qlang 采用“值语义优先 + 所有权推断 + 受控共享”的混合模型。

默认规则

  • 普通值默认按值传递
  • 复杂对象默认仍遵循唯一所有权
  • 编译器自动决定移动、借用或复制
  • 只有在跨任务共享、引用计数或 FFI 边界时,才显式引入共享语义

编译器负责的复杂工作

  • 自动借用
  • 非逃逸值的栈分配
  • 短生命周期对象的区域分配
  • 移动后使用检查
  • 析构顺序推导

共享与可变性

为了防止“看似简单,实际到处别名”的问题,Qlang 不鼓励裸共享可变引用。

推荐模型

  • 单线程局部可变:var
  • 跨作用域共享只读:Shared[T]
  • 需要循环引用:Shared[T] + Weak[T]
  • 并发共享可变:Mutex[T], RwLock[T], Atom[T]
  • 更高层并发隔离:actor

为什么这样设计

这能把“共享”和“可变”拆开,让数据竞争分析有稳定基础,也使 LSP 能给出更可靠的修改影响提示。

析构与资源释放

Qlang 应支持确定性析构:

  • 作用域退出时自动释放拥有的资源
  • defer 用于显式清理顺序控制
  • FFI 资源需要通过包装类型接入析构机制

这对文件句柄、套接字、锁、映射内存尤其重要。

与 GC 的关系

Qlang 核心语言不引入必选 GC。原因很直接:

  • FFI 成本更高
  • 布局与生命周期不够可控
  • 系统编程场景中容易引入延迟不确定性

但是这不意味着完全拒绝托管能力。后续可以通过库和编译器插件提供可选区域分配器、arena、pool 或受控 GC 域,而不是把 GC 做成所有程序的强制前提。

与 Rust 的差异化

Qlang 不是要否定 Rust 的所有权模型,而是想在工程体验上做一次再平衡:

  • 生命周期尽可能不出现在日常代码里
  • 自动借用比显式借用更常见
  • 诊断要更偏向“告诉你怎么改”
  • 公共 API 保留足够强的语义约束,内部实现尽量少仪式感

需要尽早验证的高风险点

  • 所有权推断是否会造成报错不稳定
  • 区域分配与异步任务边界如何兼容
  • Shared 与对象布局优化之间如何平衡
  • FFI 句柄包装在零成本和安全性之间如何权衡

这些问题会在原型阶段通过小型编译实验尽早打样,而不是拖到后面一次性爆炸。

Qlang research repository