程序从 main 开始
最小的示例是 examples/hello.0:
pub fn main Void world World ! check world.out.write "hello from zero\n"pub 导出入口点。fn 声明一个函数。main 接收一个 World 能力,而不是使用隐藏的全局变量。
Void 是返回类型,! 表示该函数可能失败。
运行:
zero check examples/hello.0效果使用能力
在 Zero 中,输出不是魔法。程序通过 world.out 进行写入:
check world.out.write "hello from zero\n"write 可能失败,因此使用 check 调用它。使用 check 的函数需要声明 !。
用 let 绑定值
examples/hello-let.0 引入了一个局部绑定:
pub fn main Void world World ! let message "hello from a binding\n" check world.out.write message当一个值不应改变时,使用 let。只有当值需要被有意重新赋值时,才使用 mut。
编写函数
examples/add.0 定义了一个辅助函数并在 main 中调用它:
fn answer i32 ret + 40 2 pub fn main Void world World ! let value answer() if == value 42 check world.out.write "math works\n" else check world.out.write "math broke\n"函数签名以返回类型开头,然后是参数名/类型对。当你想要带值离开函数时,使用 ret。
当前原生编译器支持显式整数宽度:
i8 i16 i32 i64
u8 u16 u32 u64
usize isize整数字面量支持十进制、0x 十六进制、0b 二进制、0o 八进制、_ 分隔符,以及 _u8 或 _usize 等后缀。
字面量会根据其上下文进行检查。let byte u8 255 可以工作。let byte u8 256 会在 zero check 时失败。
已有的整数值保持其确切类型。当你有意在原始整数类型之间进行转换时,使用 as:
let count u32 0x12c_u32let byte u8 count as u8当前的类型转换支持仅限于整数到整数的转换。
f32 和 f64 可用于十进制浮点字面量。无类型浮点字面量默认为 f64:
let ratio f64 1.0e-3let small f32 0.5let total + ratio 2.0浮点数不会与整数隐式混合,也不会在不同宽度之间隐式混合。
char 也可用作一种独特的字节大小原始类型,用于单引号字节字面量。它不能与整数互相转换:
let letter char 'A'let newline char '\n'let same == letter '\x41'f16、Unicode 标量字面量以及非整数值的类型转换不属于当前的公开接口。
使用控制流
Zero 拥有普通的 if / else 块:
if == value 42 check world.out.write "math works\n"else check world.out.write "math broke\n"当前原生子集还支持 while 循环:
while keepGoing check world.out.write "loop\n"当你需要整数计数器时,使用范围 for:
for index in 0..4 if == index 2 continue check world.out.write "tick\n"使用 break 离开最近的循环,使用 continue 跳到下一次迭代。
条件必须是 Bool,因此要显式比较值,而不是依赖真值整数。
优先使用直接条件和显式状态。检查器会拒绝向不可变绑定赋值,因此只有当循环或算法确实需要修改状态时才引入 mut。
用 type 建模数据
使用 type 定义命名记录。examples/point.0 定义了一个点并将其传递给辅助函数:
type Point x i32 y i32 fn sum i32 point Point ret + point.x point.y pub fn main Void world World ! let point Point . x 40 y 2 let total sum point if == total 42 check world.out.write "point works\n"类型字面量需要命名其字段。字段访问使用 value.field。
使用字段默认值
类型可以为调用者可能省略的字段提供默认值:
type Counter value i32 0 let counter Counter .默认值会降级为普通的具体初始化器。如果字段没有默认值,类型字面量必须显式初始化它。
用 enum 和 choice 表示备选方案
使用 enum 表示一组固定的名称:
enum Status ready failed当备选方案可能携带载荷时,使用 choice:
choice Result ok i32 err Stringexamples/result-choice.0 构造了一个带载荷的 choice 并对其进行匹配:
let result Result Result.ok 42match result ok value if == value 42 check world.out.write "choice ok\n" err message check world.out.write "choice err\n"匹配必须是穷尽的。如果一个 choice 有 ok 和 err,则必须处理两者。将载荷名称放在分支名称之后,以便在该分支内绑定它。
导入标准库模块
使用 use 导入标准库模块。examples/codec-varint.0 使用了 std.codec:
use std.codec pub fn main Void world World ! let len std.codec.encodedVarintLen 300 let checksum std.codec.crc32 "zero" if && (== len 2) (> checksum 0) check world.out.write "codec primitives ok\n"examples/parse-cursor.0 使用了 std.parse:
use std.parse pub fn main Void world World ! let digit std.parse.isAsciiDigit "7" let ident std.parse.isIdentifierStart "_" if && digit ident check world.out.write "parse primitives ok\n"当前原生编译器支持来自 std.mem、std.codec、std.parse 以及专注于持续时间的 std.time 的早期辅助工具。
编解码辅助工具现在返回其文档中记录的宽度,例如 std.codec.readU16(...) -> u16。
面向 CLI 的辅助工具也可用:
pub fn main Void world World ! let first std.args.get 1 if first.has let written std.fs.write ".zero/out/name.txt" first.value if > written 0 check world.out.write "wrote argument\n"std.args.get 返回 Maybe<String>,因为请求的参数可能不存在。
当前的 std.fs 辅助工具是托管 API。当你需要显式的 Fs、File 和 owned<File> 资源示例时,请参考标准库文档。
组织包
包有一个 zero.json 清单文件和 src/ 目录下的源文件。
{
"package": { "name": "systems-package", "version": "0.1.0" },
"targets": { "cli": { "kind": "exe", "main": "src/main.0" } }
}examples/systems-package/src/main.0 导入模块和本地声明:
use std.codecuse std.parseuse std.time pub fn main Void world World ! defer cleanup() let current Status status() let result Result Result.ok let word std.codec.readU32 "abcd" let digits std.parse.scanDigits "123abc" let duration std.time.add (std.time.ms 5) (std.time.seconds 1) if && (&& (== digits 3) (> word 0)) (> (std.time.asMsFloor duration) 0) check world.out.write "systems package\n"检查包:
zero check examples/systems-package运行测试
Zero 测试块与源代码放在一起:
test "addition is stable" expect (== (+ 40 2) 42)运行测试:
zero test conformance/native/pass/test-blocks.0
zero test --json --filter addition conformance/native/pass/test-blocks.0失败的测试会包含失败测试名称并以非零退出码退出。
检查跨平台目标
目标名称是显式的。使用 zero targets 查看支持情况,然后将 --target 传递给 check、build、graph 或 size:
zero targets
zero check --target linux-musl-x64 examples/memory-package
zero build --target linux-musl-x64 examples/memory-package --out .zero/out/memory-package检查器会拒绝不可用的能力,例如在目标中立构建上的托管 std.fs。
使用诊断
诊断功能已足够稳定,可供人类和代理使用:
zero check --json conformance/check/fail/unknown-name.0
zero explain NAM003
zero fix --plan --json conformance/check/fail/unknown-name.0每条 JSON 诊断包含代码、范围、期望/实际字段、帮助、修复安全性和修复元数据。
理解 defer 清理
defer cleanup() 将清理安排在当前作用域结束时执行:
pub fn main Void world World ! defer cleanup() check world.out.write "work\n"使用 defer 处理在作用域退出时应执行的清理工作,包括通过 ret、break 和 continue 退出的情况。
当 T 定义了规范的非抛出 fn drop Void self mutref<Self> 时,存活的 owned<T> 局部变量也会在作用域结束时被清理。直接的用户调用如 value.drop() 仍然被拒绝,以保持清理的确定性。
阅读面向内存的类型
一些示例引入了底层 Zero 代码使用的术语:
type BufferView bytes Span<u8> pub fn main Void world World ! let bytes Span<u8> std.mem.span "zero" let view BufferView . bytes bytes if && (== (std.mem.len view.bytes) 4) (== view.bytes[0] 122) check world.out.write "span ok\n"常用术语:
Span<T>是对连续值的只读视图。MutSpan<T>是对可变固定数组存储的显式可写视图。- 当前可运行的布局包括
Span<T>、MutSpan<T>和单元素[N]T。 - 索引支持 span、固定数组和面向字节的
String值。 - 切片是半开区间:
start..end、start..、..end和..。 - 索引和切片会生成边界陷阱。
- 索引左值可用于可变类型字段、固定数组和
MutSpan<T>。 - 无分配辅助工具包括
std.mem.span、泛型std.mem.len和std.mem.eqlBytes。 Maybe<T>表示可能不存在的值。ref<T>和mutref<T>使引用可变性显式化。Alloc是分配器能力。当前的分配辅助工具保持显式,仅限于文档中记录的分配器支持 API。
你不需要所有这些来编写 hello world,但你会在系统代码和 C 互操作中看到它们。
跨越 C 边界
使用 extern c 和 extern type 进行 C 互操作声明:
extern c "config.h" as config extern type CConfig enabled bool limit i32互操作类型应使布局和 ABI 边界清晰。对于必须匹配 C 布局的数据,使用 extern type。
接下来阅读什么
- 示例索引按学习顺序列出了示例。
- 语言参考文档记录了语法和语义。
- 原生编译器指南解释了源码构建和编译器验证。
- 诊断说明了如何阅读和使用错误。
- 实现状态说明了当前的限制。