探索所有权,
让内存管理各司其职。
Riddle 用移动语义和借用检查在编译期消除内存错误,用过程间逃逸分析把不越过栈帧的值留在栈上。不写一行生命周期标注,也不为垃圾回收付出全程代价。
struct Point {
x: i32,
y: i32,
}
fun distance_squared(point: Point) -> i32 {
point.x * point.x + point.y * point.y
}
fun main() {
let point = Point { x: 3, y: 4 };
let value = distance_squared(point);
print(value)
} - 生命周期标注
- 0 生命周期标注
- 个运行时 ABI 函数
- 5 个运行时 ABI 函数
- 个编译阶段
- 9 个编译阶段
- 后端输出
- C11 后端输出
语言特性
为什么是 Riddle
所有权、逃逸分析和确定性析构组合在一起,让安全既不靠标注,也不靠全程 GC。
所有权在编译期就说清楚了
赋值、传参和返回都会转移所有权。move checker 检查移动后使用、借用冲突,以及借用期间的赋值与移动——这些都发生在编译期,不需要任何运行时检查。
struct Foo {
x: i32,
y: i32,
}
fun main() {
let a = Foo { x: 1, y: 1 };
let b = a;
print(a); // error: a 已经被移动
print(b);
} 标量、共享引用、原始指针和命名函数项默认可复制;&mut T 与闭包值不可复制。
- 用户类型实现
std::marker::Copy即可进入复制语义,编译器会验证所有字段与枚举 payload - 字段访问本身不移动整个结构体;
match解构按字段记录部分移动,未移动的兄弟字段仍可继续使用 -
Option<T>与Result<T, E>仅在 payload 全部实现Copy时才是Copy
不写生命周期,也能返回引用
Riddle 没有生命周期语法。编译器用过程间不动点的逃逸分析,计算每个函数的“外泄参数 / 返回来源参数”摘要,据此决定局部值留在栈上还是提升到保守式非移动 GC 堆。存储位置不会放宽移动与借用检查。
struct Foo {
x: i32,
y: i32,
}
fun make_ref() -> &Foo {
let foo = Foo { x: 1, y: 2 };
&foo // 引用越过栈帧,foo 被提升到 GC 堆
} | 分析结果 | MIR 分配 |
|---|---|
| 未逃逸且不需要稳定地址的局部 | SSA 值,不生成分配指令 |
| 未逃逸但可变或被闭包按引用捕获 | Alloca,栈上存储 |
| 逃逸的局部 | HeapAlloc,GC 堆存储 |
| 未逃逸 / 逃逸的闭包环境 | Alloca / HeapAlloc |
GC 只负责地址,析构仍然确定
实现 `std::ops::Drop` 的类型会在所有者作用域结束时确定性地运行析构。逃逸到 GC 堆只改变值的地址,不改变析构时机;drop flag 保证移动之后不会重复析构。
struct FileHandle {
raw: i32,
}
impl Drop for FileHandle {
fun drop(&mut self) {
// 释放 raw 对应的外部资源
}
} 局部变量、参数、模式绑定、迭代元素、聚合字段和闭包环境都在析构覆盖范围内。
-
Drop + Copy、直接调用析构方法、从显式Drop类型移出字段都会被拒绝 -
for循环的当前元素、迭代器和提前退出路径拥有各自独立的析构作用域
抽象不必付出运行时代价
trait 支持默认方法、关联类型与默认关联类型、父 trait 声明与传递 bound。泛型在 C 后端单态化,const generics 让长度直接进入类型。
trait Summary {
fun title(&self) -> &str;
fun summarize(&self) -> &str {
self.title()
}
}
struct Buffer<T, const N: usize> {
data: [T; N],
} impl 未覆写时使用 trait 默认方法体,显式覆写优先。
-
<T: A + B>与where子句;impl 上的where会检查 Paterson condition - 运算符通过
#[lang = "..."]trait 分派到用户类型;标量运算直接降为原生 C 运算,不生成包装函数 -
==/!=检查PartialEq,有序比较检查PartialOrd,支持异构右操作数 impl
漏掉的分支,编译器会告诉你在哪
`match` 对枚举、布尔值、`()`、整数、元组和结构体做递归穷尽性检查。非穷尽的整数匹配不是笼统报错,而是直接报告未覆盖的连续值区间。
fun classify(n: i32) -> i32 {
match n {
x if x < 0 => -1,
0 => 0,
_ => 1,
}
} 穷尽性缺口以 E0039 报告,并列出未覆盖的取值区间。
- guard 失败后继续检查后续 arm;带 guard 的 arm 不计入静态穷尽性
- 支持枚举 unit / tuple / struct 变体模式,payload 绑定会进入 guard 和 arm 表达式
和 C 世界打交道,边界写在代码里
`unsafe extern "C"` 块内的导入默认不安全,可以用 `safe fun` 逐个声明确实安全的函数。C 后端不按函数名提供任何内置 helper,所有 `extern "C"` 声明都按普通外部符号生成。
unsafe extern "C" {
safe fun abs(x: i32) -> i32;
fun malloc(size: usize) -> *mut u8;
}
fun main() {
let value = abs(-42);
let pointer = unsafe { malloc(16) };
} 原始指针的解引用和索引需要处在 unsafe 上下文中。
- C 导入中的
&str映射为const char*;带函数体的导出定义保留{ ptr, len }胖指针 -
unsafe fun与unsafe fun(...) -> T函数类型,支持单向安全函数转换
编译流程
从源码到 C,九个阶段
riddlec 已经能跑通完整前端和 C 后端。每个阶段都被仓库中的测试覆盖。
- 01
词法与语法分析
IncrementalParser提供局部重解析 API - 02
AST 包装
统一的语法树表示,属性随项一起进入 AST
- 03
HIR 降级
含 E0040 / E0050 / E0051 / E0052 诊断
- 04
作用域图与名字解析
基于片段的增量作用域图,支持部分失效
- 05
类型检查
可复用的
IncrementalTypeChecker - 06
逃逸分析
过程间不动点,决定栈分配还是 GC 堆分配
- 07
move checker
移动后使用、借用冲突、借用期间的赋值与移动
- 08
MIR 降级
SSA 形式,Phi 节点、基本块、
Alloca/HeapAlloc - 09
C 后端代码生成
输出调用
rgcABI 的 C11 代码
不指定后端时,riddlec 在完成 move / borrow 检查后停止,只有需要生成后端代码时才继续降级 MIR。
运行时
五个函数,组成整个运行时 ABI
每个 runtime provider 都要实现这五个符号:初始化栈、分配、调整和释放内存,以及触发回收。
void rgc_init(void *stack_bottom);
void *rgc_alloc(size_t size);
void *rgc_realloc(void *ptr, size_t size);
void rgc_free(void *ptr);
void rgc_collect(void); crates/gc 提供默认的非移动、保守式 mark-sweep 实现;也可以在 Clue.toml 的 [runtime].source 指向自己的 provider。
- 不依赖 Boehm GC
clue build直接用系统 C 编译器编译生成的 C 与运行时源码,没有额外的第三方运行时依赖。- 保守式、非移动
- 只有逃逸分析判定会越过当前栈帧的值才进入 GC 堆;其余值留在栈上,回收器不移动对象。
- 分配接口各司其职
rgc_realloc为Vector扩容,rgc_free释放 provider 管理的内存;没有 GC 的 provider 也可以忽略栈底并把rgc_collect实现为空操作。- 完全可替换
- 运行时 provider 由
clue选择,也接受自定义 provider——嵌入式或特殊场景可以自带分配器。
v0.2.3 新内容
更有表达力的语言与更宽的标准库
这一版加入括号 lambda、vec! 与 range 表达式、迭代器适配器、新的文件 / 时间 / 随机数 / 解析模块,LSP 也带来九个快速修复与 Clue.toml 支持。
- 01
括号 lambda
[v -> v * 2]用短语法写匿名函数,参数与返回类型自动推断,values.map [v -> v * 2]可以就地调用;它与fun闭包共享同一底层表示,vec![a, b]和0..5range 一并到位。 - 02
更宽的标准库
集合获得
remove、insert和sort;迭代器新增惰性map/filter/zip与即时count/fold/find;新的std::fs、std::time、std::random、std::parse模块覆盖文件、休眠、随机数与数字解析。 - 03
更敏锐的语言服务器
九个由诊断驱动的快速修复、lambda 参数与方法链类型提示、
Clue.tomlschema 诊断与补全、整理导入、扩展选区、文档链接与拉取式诊断。 - 04
编译器地基
riddlec支持多输入文件,常量在编译期求值用于数组长度与 const generics,切片以指针/长度对跨越unsafe extern "C",GC 改用哈希对象注册表并自适应调整回收阈值。
工具链
一个管理器,四个二进制
ridup 管理 Riddle 的版本,一条工具链里是 clue、riddlec、riddle 和 riddle-lsp。预编译版本可从 GitHub Releases 下载;从源码安装需要较新的 Rust stable。
ridup
工具链管理器
选择并运行已安装的工具链,stable、nightly 和 canary 三个通道各装一份,互不覆盖。它只管 Riddle 版本,C 编译器仍由选中的 clue 在本机寻找。
ridup toolchain install stable | nightly | canary
-
stable和nightly取 GitHub Release 归档,校验 SHA-256 后才替换旧工具链 -
canary拉main最新提交,在本机cargo build --workspace --release,只需要 Rust 和 Cargo -
ridup toolchain link dev <目录>直接把本地构建目录当成一条工具链 -
riddle-toolchain.toml、RIDUP_TOOLCHAIN和clue +dev build都能改变选择 - 把 ridup 复制或硬链接成
clue、riddlec、riddle、riddle-lsp后,它会代理到选中的工具链 - 下载与 Canary 构建都遵守
HTTPS_PROXY等标准代理环境变量
riddlec
编译器 CLI
检查 Riddle 源码并生成 C,前端检查与 C 后端都在同一个二进制里。
riddlec [--verbose] [--backend c] [--target <triple>] [--output <file>] <file>...
-
--backend c生成调用rgcABI 的 C 代码 -
--target <triple>选择目标平台,也可通过RIDDLE_TARGET或Clue.toml设置 - 自动把
std/lib.rid拼到用户源码后面 - 不指定后端时只做前端检查,不降级 MIR
riddle
统一工具 CLI
提供 Riddle 工具链的统一入口,当前包含源码格式化命令。
riddle fmt [--emit files|stdout|check] [--check] <file>...
- 格式化文件或标准输入,也可以只检查而不写回
-
--tab-size设置缩进宽度,--hard-tabs使用制表符 - 与 LSP 的
textDocument/formatting复用同一 formatter
clue
项目构建器
创建、检查、构建和运行 Riddle 项目。二进制项目会生成本机可执行文件,库项目可输出 .rlib / .rmeta 元数据以及静态库或动态库。
clue init | new | check | build | run [--target <triple>]
- 设置
CC时严格使用它,否则自动寻找可完成 C11 编译链接的系统编译器 -
--target、RIDDLE_TARGET和Clue.toml的[build].target依次覆盖宿主平台 -
ridup target add <triple>安装目标 runtime;链接仍需要目标 C 工具链 - path、git 和 registry 依赖统一解析进 lockfile,固定确切版本
-
clue build会保留.clue/build/<name>.c,方便检查生成结果 -
Clue.toml的[runtime].source可指向自定义运行时实现
riddle-lsp
语言服务器
基于 tower-lsp。解析错误、HIR 诊断、类型检查错误、move / escape 诊断全部通过 LSP 推送。
- 项目范围补全,优先使用所有已打开文件的未保存内容
- 语义 Token 区分自由函数、方法、struct、enum 和 trait
- 错误码可跳转到错误码手册,注释与修复建议以
note:/help:附加
编辑器支持
在你已经在用的编辑器里
仓库的 editors 目录提供开箱即用的适配配置。
- Helix
- VS Code
- Zed
- IntelliJ IDEA 2026.1+
现在就能用
-
.rid文件识别 - Clue 项目、未保存文件和未打开模块的诊断
- 解析、类型、move / borrow 诊断
- 函数、方法、struct、enum、trait、参数和可变绑定的语义高亮
- 跨模块返回类型的局部变量 Inlay Hint
- 跨文件补全,含字段、实例方法、枚举变体和关联函数
- 可变闭包绑定 Code Action
- 增量文档同步与 Semantic Token delta
- Hover、跳转定义与实现、查找引用、重命名与格式化
- 工作区索引与自动导入
仍在完善
- 更丰富的语义重构代码操作
开始使用
五条命令,跑起第一个程序
用 ridup 装工具链并在通道之间切换,也可以从 GitHub Releases 下载压缩包加入 PATH。
- 1
安装工具链
装完通道要再选一次默认工具链;之后
ridup show会告诉你当前用的是哪条,以及为什么是它。cargo install --git https://github.com/riddle-lang/ridup ridup toolchain install stable ridup default stable - 2
创建并运行项目
clue run会先完成与clue build相同的构建,再运行生成的可执行文件。clue new hello cd hello clue check clue build clue run
clue build 会保留 .clue/build/hello.c。设置 CC 时 Clue 严格使用它;否则自动寻找 cc、gcc、clang 及其版本化命令,Windows 还支持 clang-cl 和 cl。想直接敲 clue 或 riddle,把 ridup 复制或硬链接成对应名称即可;不做代理时用 ridup run stable clue new hello。
项目状态
这是技术预览,我们不藏着
v0.2.3 的语言和工具链仍可能发生不兼容变化。下面是当前真实的能力边界。
已经可用
- 类型检查、move checker、借用与逃逸分析
- 泛型、const generics、trait 与关联类型
- 闭包与
Fn/FnMut/FnOnce调用能力检查 - 递归穷尽性检查的
match -
IntoIterator/Iterator驱动的for -
unsafe语义与 C FFI - 数组、切片、字符串与 Vector 标准库
- Drop、运算符重载与确定性 C backend
- 元组类型、tuple 模式与 tuple 枚举变体
- 过程宏与标准 derive(含 Default、Hash、Ord 等)
- 支持位置、命名与 debug 占位符的格式化输出
- 括号 lambda
[v -> v * 2]与fun闭包并存 - 迭代器适配器、集合删除与
vec! - 标准库的文件、时间、随机数与解析模块
-
riddle fmt源码格式化 CLI,与 LSP 共享 formatter - 七个目标 triple 的交叉构建与 target runtime
- 内置标准库、C11 代码生成、项目工具与 LSP
当前限制
- 格式化已支持位置、命名与
:?占位符;宽度、对齐和填充说明符尚未实现 - 浮点余数尚未支持,
Rem/RemAssign目前只为整数实现 - 泛型偏向单态化,尚未覆盖完整 Rust 泛型能力
- 只有 C 后端;生成可执行文件依赖系统 C 编译器
- 逃逸分析粒度是整个局部变量,不做字段级拆分
- 闭包按整个绑定捕获,不做字段级精确捕获
- 没有裸函数指针类型;可调用值需用
impl Fn(...) -> T或dyn Fn(...)声明
不保证语法和 ABI 稳定。请把 Riddle 当作可以认真试用、但还不能上生产的语言。
发展规划
接下来要做什么
这份路线图就是项目当前真实的优先级排序:正确性最先,语言能力其次,工具链与生态随后。顺序会随反馈调整,不构成时间承诺。
语言核心
正确性优先,先把安全语义的地基打完。
- 带标签的
break/continue,以及 or / range / slice 模式
标准库与工具链
语言能写的东西多了,库和工具要跟上。
- 标准库扩充:缓冲 IO、完整格式说明符与更强的解析能力
核心稳定之后
这些方向明确排在核心语义稳定之后,现在不抢跑。
- 并发与 async / await
- C 之外的其他后端
- 语法与运行时 ABI 的稳定化承诺
想影响优先级?到 GitHub Issues 告诉我们你最需要哪一项。