编写文件
打开编辑器,把下面五行保存为 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 时:
- 编译器解析你的文件并做类型检查。
- 它为你的平台生成一个小巧的原生可执行文件。
- 运行时为当前进程构造一个
World能力对象。 - 它调用
main(world)。 - 你的代码把 "hello from zero\n" 写入这个 world 的
out流,这个流连接着你终端的 stdout。 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 这样的命令调用。