RGC 是一门 Python 风格、编译型、内存安全 的系统编程语言。语法上你几乎不用重新学习——def、if、for、and、None 都还在;但它在编译期完成类型检查和所有权检查,由 LLVM 生成原生机器码,性能接近 C / Rust。

这篇教程面向 Python 开发者,从零开始:先跑起第一个程序,再逐个掌握语法、类型、所有权、泛型、模块与标准库,最后用一个成绩管理器小项目把全部知识串起来。所有示例都来自仓库中可编译运行的代码,你可以直接复制运行。

1. 环境准备

1.1 构建编译器

RGC 采用自举(bootstrapping)构建:stage0 是 C++ 编写的一次性种子,之后由 RGC 自身逐级编译出 stage1 → stage2 → stage3,最终编译器完全由 RGC 构建。

先安装依赖:

  • LLVM 23(自包含发行包,默认路径 /home/bluemoon/llvm/LLVM-23.1.4-Linux-X64/bin)
  • CMake ≥ 3.31
  • GCC 13.3+ 或 Clang

克隆仓库并构建:

git clone https://github.com/rgc-lang
cd RGLite
cd src
./build.sh

首次构建会自动编译 C++ 引导种子;之后 rack 会管理 rgc1 → rgc2 → rgc3 的逐级自举,并校验 IR 不动点。构建产物在 src/build/rgc3。

1.2 编译并运行第一个程序

cd src
./build/rgc3 ../examples/hello.rg -o /tmp/hello
/tmp/hello

输出:

hello from RGC, x + y = 42
condition: ok
score 95 => excellent
score 70 => pass
score 30 => fail
flag = True
7 // 2 = 3  7 % 2 = 1  2 ** 10 = 1024

rgc3 内嵌 LLVM,运行时只需要系统链接器 gcc,不需要额外安装 llc 或 clang。

2. 第一个程序:Hello World

来看 examples/hello.rg 的完整内容:

def add(a: int, b: int) -> int {
    return a + b
}

def classify(score: int) -> str {
    if score >= 90 {
        return "excellent"
    } elif score >= 60 {
        return "pass"
    } else {
        return "fail"
    }
}

def main() {
    x = 40
    y: int = 2
    total = add(x, y)
    print("hello from RGC, x + y =", total)

    if x > 10 and y < 10 {
        print("condition: ok")
    } else {
        print("condition: no")
    }

    flag = not (x == 0)
    print("flag =", flag)
    print("7 // 2 =", 7 // 2, " 7 % 2 =", 7 % 2, " 2 ** 10 =", 2 ** 10)
}

即使没有 Python 背景,这段代码也几乎可以无障碍阅读。几个要点:

  • 入口函数是 main(),和 Python 脚本一样。
  • 变量 x = 40 无需 mut——RGC 的变量默认可变,符合 Python 习惯。
  • y: int = 2 是显式类型注解;x 则由编译器推断为 int。
  • 块结构用大括号 {},不依赖缩进,注释是 #。
  • 逻辑运算符沿用 Python 风格:and、or、not。
  • //(整除)、%(取模)、**(幂)都与 Python 一致。

注意一个与 Python 不同的细节:两个整数相除 / 得到整数(与 C 一致),浮点数参与才会得到浮点结果。例如 7 / 2 结果是 3,而 7.0 / 2 是 3.5。

3. 变量与类型系统

RGC 是静态类型语言:每个表达式在编译期都有确定的类型。但类型推断让你不必到处写注解。

类型 说明 示例
int 整数 42
float 浮点数 3.14
bool 布尔值 True / False
str 字符串 "hello"
None 空值 None
def main() {
    count = 1          # 推断为 int
    name = "RGC"       # 推断为 str
    ratio: float = 0.5 # 显式注解为 float
    ok: bool = True
}

函数签名可以完整标注参数与返回类型,让错误尽早暴露在编译期:

def scale(x: float, factor: int) -> float {
    return x * factor
}

4. 函数与控制流

4.1 函数

def greet(name: str) {
    print("Hello,", name)
}

返回值类型可以省略;需要返回时用 return。

4.2 if / elif / else

def classify(n: int) -> str {
    if n < 0 {
        return "negative"
    } elif n == 0 {
        return "zero"
    } else {
        return "positive"
    }
}

4.3 循环:for / while / break / continue / pass

def main() {
    # for 遍历 range
    for i in range(5) {        # 0 1 2 3 4
        print(i)
    }

    # while + continue + break
    i = 0
    while i < 6 {
        i = i + 1
        if i == 2 {
            continue
        }
        if i == 5 {
            break
        }
        print("i =", i)
    }

    # pass 空语句
    n = 0
    while n < 3 {
        n = n + 1
        pass
    }
}

range、break、continue、pass 的语义与 Python 一致,只是块结构换成大括号。

5. 类、枚举与模式匹配

RGC 提供完整的代数数据类型(ADT):class 组织数据与方法,enum 描述"要么是 A、要么是 B"的枚举,配合 match 做穷尽的模式匹配。

class Point {
    x: int
    y: int
    def sum(self) -> int {
        return self.x + self.y
    }
}

enum Shape {
    Circle(float)
    Rect { w: float, h: float }
}

def area(s: Shape) -> float {
    match s {
        case Circle(r) => 3.14159 * r * r
        case Rect { w, h } => w * h
        case _ => 0.0
    }
}

def main() {
    p = Point()
    p.x = 3
    p.y = 4
    print("p.sum() =", p.sum())   # 7

    c = Circle(2.0)
    r = Rect(3.0, 4.0)
    print("area(circle) =", area(c))
    print("area(rect)  =", area(r))
}

几个和 Python 不一样的写法:

  • 枚举变体用构造函数语法创建:Circle(2.0)、Rect(3.0, 4.0);没有参数的单位变体写成 Pass()。
  • match 的 case 不需要括号:case Circle(r) 直接把负载绑定到变量 r。
  • case _ 是通配分支,保证匹配一定完整。

6. 所有权与借用

这是 RGC 内存安全的核心,也是和 Python 最大的不同。每个值有唯一的拥有者,离开作用域即被释放;通过借用安全地共享访问。全程没有 GC,零运行时开销。

6.1 移动(move)

自定义类型(class / enum)默认是 move 语义。赋值后原变量不再可用:

class Point {
    x: int
    def sum(self) -> int {
        return self.x
    }
}

def main() {
    a = Point()
    a.x = 5
    b = a              # 所有权从 a 移动到 b
    print("b.x =", b.x)   # 5
    # print(a)         # 编译错误:a 已被 move
}

被移动的变量在重新赋值后可以恢复可用:

d = Point()
d.x = 7
e = d                # move d -> e
d = Point()          # 重新赋值,d 恢复可用
d.x = 8

6.2 只读借用 @ 与可变借用 @var

不想转移所有权、只想读,用 @ 借用。以自定义类型为例:

class Counter {
    value: int
}

def read(c: @Counter) -> int {
    return c.value
}

def main() {
    x = Counter()
    x.value = 10

    r1 = @x       # 只读借用
    r2 = @x       # 多个 @ 可以并存
    print("read(r1) =", read(r1))   # 10
    print("read(r2) =", read(r2))   # 10
    print("x.value =", x.value)     # x 仍然可用
}

想修改,用 @var 可变借用(独占访问):

def push_zero(lst: @var List[int]) {
    lst.push(0)
}

多个 @ 只读借用可以并存;@var 与任何其他借用互斥——这一切都在编译期检查,保证没有 use-after-move、悬垂引用和数据竞争。

6.3 方法调用自动借用

调用 self 方法时,方法按借用语义自动处理,不需要手动写 @。可变方法自动使用可变借用:

class Counter {
    value: int
    def inc(self) {
        self.value = self.value + 1
    }
}

def main() {
    z = Counter()
    z.value = 30
    z.inc()           # self 自动可变借用
    print(z.value)    # 31,z 仍可用
}

7. 泛型与接口

7.1 泛型函数

def identity[T](x: T) -> T {
    return x
}

def pair_sum[T](a: T, b: T) -> T {
    return a + b
}

def main() {
    print(identity(42))        # 42
    print(identity(3.14))      # 3.14
    print(pair_sum(2, 3))      # 5
    print(pair_sum(1.5, 2.5))  # 4.0
}

7.2 泛型类

class Box[T] {
    value: T
    def get(self) -> T {
        return self.value
    }
}

def main() {
    b = Box[int]()
    b.value = 42
    print(b.get())        # 42

    f = Box[float]()
    f.value = 3.14
    print(f.get())        # 3.14
}

7.3 接口与边界约束

接口定义行为契约,类通过 class T : Interface 实现:

interface Show {
    def show(self) -> str
}

class Point : Show {
    x: int
    y: int
    def show(self) -> str {
        return "Point"
    }
}

class Line : Show {
    a: int
    b: int
    def show(self) -> str {
        return "Line"
    }
}

# T 受 Show 约束,函数体内可直接调用接口方法
def describe[T: Show](x: T) -> str {
    return x.show()
}

def main() {
    p = Point()
    p.x = 1
    p.y = 2
    print(describe(p))   # Point

    l = Line()
    l.a = 3
    l.b = 4
    print(describe(l))   # Line
}

泛型通过单态化编译:每个具体类型生成专用代码,没有虚表、没有装箱,性能和手写专用实现一致。

8. 模块系统

用 import 引入模块,模块内部通过 public 控制可见性。未加 public 的项只在模块内部可见。

geometry.rg:

public class Point {
    x: int
    y: int
    def sum(self) -> int {
        return self.x + self.y
    }
}

# 私有函数:仅模块内部可见
def double(v: int) -> int {
    return v * 2
}

public def make_point(x: int, y: int) -> Point {
    p = Point()
    p.x = x
    p.y = y
    return p
}

public def norm_sq(p: Point) -> int {
    return double(p.x * p.x + p.y * p.y)
}

main.rg:

import geometry

def main() {
    p = make_point(3, 4)
    print("point sum =", p.sum())   # 7
    print("norm_sq   =", norm_sq(p))  # 50
}

注意 norm_sq 内部调用了私有函数 double,但对外只暴露 public 项——内部实现细节被安全地封装在模块边界内。

9. 标准库初探

标准库目前提供内建核心类型与方法:to_string、字符串方法、List[T]、Maybe[T]、Result[T, E]。

9.1 字符串

def main() {
    msg = "Hello, " + "RGC"
    print(msg.len())                  # 10
    print(msg.contains("lo,"))        # True
    print(msg.startswith("Hello"))    # True
    print(msg.endswith("RGC"))        # True

    print("hello world".slice(0, 5))  # hello
    print("12345".to_int().unwrap())  # 12345
}

9.2 List

def main() {
    xs = List[int]()
    xs.push(1)
    xs.push(2)
    xs.push(3)
    print(xs.len())          # 3
    print(xs.get(0))         # 1
    xs.set(1, 20)
    print(xs.get(1))         # 20
    print(xs.contains(3))    # True
    print(xs.pop())          # 3

    total = 0
    for x in xs {
        total = total + x
    }
    print(total)             # 21
}

9.3 Maybe 与 Result

RGC 用 Result[T, Err] 表达可恢复错误:不抛异常、不静默失败。解析字符串可能失败时,签名里就写着"可能失败":

def main() {
    ok = Result[int, str].Ok(7)
    err = Result[int, str].Err("boom")
    print(ok.is_ok())    # True
    print(err.is_err())  # True

    # match 推荐用法:错误分支显式处理
    match err {
        case Ok(v) => print("unexpected:", v)
        case Err(msg) => print("error is:", msg)
    }
}

配合字符串的 to_int():

r = "12345".to_int()
match r {
    case Ok(v) => print("value =", v)     # value = 12345
    case Err(msg) => print("err:", msg)
}

bad = "abc".to_int()
match bad {
    case Ok(v) => print("unexpected:", v)
    case Err(msg) => print("to_int error:", msg)   # invalid integer literal
}

10. 实战:成绩管理器

现在把上面的知识串起来,写一个小程序:录入三名学生成绩,输出每个人的等级、及格人数和平均分。这会用到类、接口、枚举、模式匹配、List、所有权与借用、泛型边界。

interface Show {
    def show(self) -> str
}

class Student : Show {
    name: str
    score: int
    def show(self) -> str {
        return self.name + ": " + to_string(self.score)
    }
    def is_pass(self) -> bool {
        return self.score >= 60
    }
}

enum Grade {
    Excellent
    Pass
    Fail
}

def grade_of(score: int) -> Grade {
    if score >= 90 {
        return Excellent()
    } elif score >= 60 {
        return Pass()
    } else {
        return Fail()
    }
}

def grade_name(g: Grade) -> str {
    match g {
        case Excellent => "优秀"
        case Pass => "及格"
        case Fail => "不及格"
        case _ => "?"
    }
}

def total(students: @List[Student]) -> int {
    sum = 0
    for s in students {
        sum = sum + s.score
    }
    return sum
}

def count_pass(students: @List[Student]) -> int {
    n = 0
    for s in students {
        if s.is_pass() {
            n = n + 1
        }
    }
    return n
}

def main() {
    students = List[Student]()

    s = Student()
    s.name = "Alice"
    s.score = 92
    students.push(s)          # push 移动所有权,s 可重新赋值

    s = Student()
    s.name = "Bob"
    s.score = 74
    students.push(s)

    s = Student()
    s.name = "Carol"
    s.score = 55
    students.push(s)

    for st in students {
        print(st.show(), "=>", grade_name(grade_of(st.score)))
    }

    print("及格人数 =", count_pass(@students))
    print("平均分 =", total(@students) / students.len())
}

运行输出:

Alice: 92 => 优秀
Bob: 74 => 及格
Carol: 55 => 不及格
及格人数 = 2
平均分 = 73

几个值得回味的点:

  • students.push(s) 把 s 移动进列表;之后给 s 重新赋值一个新 Student(),符合所有权模型。
  • for st in students 按借用迭代,循环结束后 students 仍然可用。
  • count_pass(@students) 和 total(@students) 用 @ 只读借用传入列表,调用后 students 不受影响。
  • grade_name 用 match 把枚举变体映射成中文标签,编译期保证分支完整。
  • class Student : Show 实现了接口,show() 在 for 循环里被多态调用(单态化,无虚表开销)。

11. 下一步

至此你已经掌握了 RGC 的核心:从语法、类型,到所有权、泛型、模块和标准库。继续深入:

RGC 以 Apache 2.0 协议开源。欢迎在 GitHub 提 Issue、贡献代码;也欢迎安装 VSCode 插件,获得语法高亮与补全支持。祝你写得开心!