Menu

Zero Hello World:编写并运行第一个 .0 程序

你的第一个 Zero 程序:经典 hello world 的每个部分是什么意思,怎样用 zero run 运行它,以及为什么一个只有五行的程序就已经用到了 World、raises 和 check。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

编写文件

打开编辑器,把下面五行保存为 hello.0,放在任何方便的位置;或者直接点击下面代码块上的“运行”:

就这些,这已经是一个完整的 Zero 程序了。要在本地运行它:

zero run hello.0

你应该会看到:

hello from zero

如果出现 command not found,请回到 安装 Zero,先确保 zero --version 能正常运行。

逐个拆解每个词

即使是这么小的程序,也已经展示了 Zero 大部分与众不同的地方。我们从左到右读一遍。

pub

pub 把声明标记为公开的,在当前模块之外也能看到。运行时需要从文件自身的作用域之外找到 main,所以 main 必须是 pub。同一个文件里的私有辅助函数就不需要它。

fun

fun 用来开始一个函数声明。Zero 用的是 fun,而不是 fn(Rust)、func(Go)或 function(JavaScript)。只有一个关键字,每次用法都一样。

main

约定俗成的入口名称。Zero 的可执行目标运行时,运行时会寻找 pub fun main(world: World) 并调用它。其他函数你想怎么命名都行,但按照约定,main 留给程序入口。

(world: World)

唯一的参数名叫 world,类型是 World。运行时会在调用 main 之前构造一个 World 值并传进来。这个值携带着程序的能力:访问 stdout、stdin、文件系统、网络等,具体取决于运行时决定授予哪些能力。

参数名由你决定,写成 (w: World) 或 (io: World) 也能正常编译。示例中的惯例是 world,我们也沿用这个写法。

-> Void

返回类型。Void 表示这个函数不返回有用的值,调用它是为了它的副作用,而不是它的返回值。你写的很多函数会返回真正的类型,比如 i32 或你自己定义的 shape。

raises

main 上单独的 raises(不指定具体错误类型)表示“这个函数可能失败”。对 main 来说,这意味着只要函数体里有任何 check 把错误向上传递,程序就可能以非零状态码退出。我们会在 Raises 和 Check 中看到更具体的形式,比如 raises { InvalidInput }。

check world.out.write("hello from zero\n")

这一行才真正做了事情。它由三部分组成:

  • world.out:标准输出流,作为 World 能力对象上的一个字段提供。
  • .write("hello from zero\n"):把字符串写入这个流的方法。它返回一个可能表示失败的结果(写入可能失败,流也可能已经关闭)。
  • check:如果发生失败,就把它向上传递。如果没有 check,编译器会报错,指出 write 的结果被悄悄丢弃了。

末尾的 \n 是一个换行符。没有它,输出不会换到新的一行,你的 shell 提示符就会和消息挤在同一行。

刚才发生了什么

当你运行 zero run hello.0 时:

  1. 编译器解析你的文件并做类型检查。
  2. 它为你的平台生成一个小巧的原生可执行文件。
  3. 运行时为当前进程构造一个 World 能力对象。
  4. 它调用 main(world)。
  5. 你的代码把 "hello from zero\n" 写入这个 world 的 out 流,这个流连接着你终端的 stdout。
  6. main 返回 Void,运行时完成清理,程序以状态码 0 退出。

没有垃圾回收器,没有隐藏的运行时初始化,也没有隐式的模块引导。整个程序就是你写的这个函数,加上它调用的标准库代码。

做个小改动

让程序先把消息绑定到一个名字上,再写出去:

再运行一次,输出一样,但现在你已经见过了 let 绑定,也确认了字符串是可以四处传递的一等值。

一个会失败的版本

如果忘了写 check 会怎样?

pub fun main(world: World) -> Void raises {
    world.out.write("oops\n")
}

zero check hello.0 会拒绝编译这段代码。write 的结果是一个可能失败的值,忽略它就是编译错误。你要么用 check 处理它(把错误向上传递),要么显式地处理它。这和 Rust 的 must_use 结果是同一个思路,只不过 Zero 对每一个可能失败的调用都强制执行。

下一步:Zero CLI

这里你用到了 zero run。CLI 只有一小组值得了解的命令:check、run、build、test、fix、explain,每个命令都有专为智能体读取而设计的结构化 --json 模式。

常见问题

Zero 的 hello world 程序是什么样的?

经典的 Zero hello world 只有五行:pub fun main(world: World) -> Void raises { check world.out.write("hello from zero\n") }。把它保存为 hello.0,然后运行 zero run hello.0。

Zero 里的 pub fun main 是什么意思?

pub 让声明变成公开的,在所属模块之外也能看到。fun 用来声明函数。main 是 Zero 在可执行目标中寻找的约定入口。合起来,pub fun main 声明的就是程序启动时运行时会调用的公开入口。

为什么 main 要接收一个 World 参数?

Zero 没有全局 I/O。任何与外部世界打交道的操作,比如 stdout、stdin、文件和网络,都要通过显式传入的能力对象完成。运行时把一个 World 值交给 main,这个值(或它的一部分)是函数做 I/O 的唯一途径。这样副作用就会直接体现在函数签名里。

hello world 里的 raises 和 check 有什么作用?

main 上的 raises 声明这个函数可能失败。check world.out.write(...) 调用一个可能失败的函数,如果失败,就把错误向上传给调用方,这里的调用方是运行时,它会以非零状态码退出。如果没有 check,编译器会拒绝编译这次调用,因为错误没有被处理。

Zero 使用什么文件扩展名?

Zero 源文件使用 .0 扩展名(是数字零,不是字母 O)。hello.0 就是一个 Zero 源文件。编译器通过 zero check hello.0 和 zero run hello.0 这样的命令调用。

Coddy 编程语言插图

和 Coddy 一起学编程

开始