Go程序员为什么喜欢Ziglang

在编程语言领域,Zig相对来说是新成员。Zig由Andrew Kelly创立,于2016年正式面世,Zig社区将其定位为“用于开发健壮、优化和可复用软件的通用编程语言”。

在这简单的描述中,蕴含着一些宏大的目标。例如,Zig被视为一个能够与C语言竞争的编程语言(Rust在Linux内核方向进展更快一些)。此外,Zig提供一整套编译工具链,可以替代现有C编译器(Rust可以使用cargo-zigbuild跨平台编译)。

作为一名Go开发者,我对Zig及其工具链的提议尤为感兴趣。在研究Zig时,发现这两种语言(Zig和Go)在某些方面有着共同的特性。在这篇博文中,将重点介绍作为Go程序员,对Zig感兴趣的一些特性。

Zig 与 Go

简洁性

两种语言都秉承简洁的设计哲学,以减少语言的干扰,使开发者更快上手并能够高效地完成开发任务。Zig中没有宏、预处理器或操作符重载等功能,避免了执行流程中的“魔法”。

Go通过运行时处理内存分配和释放。而Zig则坚持其“无隐藏控制流”的准则,没有自动内存管理,而是通过标准库提供内存管理API,让开发者管理内存。

强类型

作为一门系统编程语言,Zig围绕类型系统设计了许多功能,注重安全性和C ABI(应用二进制接口)兼容性。这里简要介绍一些有趣的特性:

  • 有符号/无符号整数(预设大小从8位到128位)
  • 任意大小的有符号/无符号整数(例如i7表示7位整数)
  • 浮点数(精度从16位到128位)
  • 切片和数组(例如 []u8{‘h’, ‘i’, ‘!’}[4]i32{1, 2, 3, 4}
  • 以UTF-8编码的字符串字面量,存储为以空字符结尾的字节数组
  • 具有丰富功能的结构体类型,可与C ABI兼容
  • 具有隐式/显式序数值的枚举,并支持方法
  • 可用于存储多种类型值的联合
  • 支持使用向量并行操作
  • 传统指针及多元素指针与切片表达式

错误处理

Zig中的错误处理机制非常有特色,融合了try-catch异常语义和Go的错误值模式。

首先,所有Zig错误都是必须分配和处理的值(否则会导致编译错误)。错误声明使用error关键字,如下所示:

1
const DigitError = error{ TooLarge, };

有趣的是,Zig错误值可以与普通类型的值结合,使用!运算符形成一个联合类型。下面的函数可以返回错误或u32类型的值:

1
2
3
4
5
fn addSingleDigits(a: u32, b: u32) !u32 {
if (a > 9) return error.TooLarge;
if (b > 9) return error.TooLarge;
return a + b;
}

此外,Zig提供了类似Java等语言的catch关键字,用于错误处理:

1
2
3
4
5
6
7
pub fn main() void {
const result = addSingleDigits(4, 5) catch |err| {
std.debug.print("Error: {}\n", .{err});
return;
};
std.debug.print("Result: {}\n", .{result});
}

Zig还支持通过try关键字将错误向上级调用传播。此外,还可以通过if-else-switch语句更精确地筛选和处理错误。

Zig测试

在Zig中,测试是语言的关键性能力,使用test关键字声明:

1
2
3
4
test "test that 1 + 1 equals 2" {
const result = 1 + 1;
assert(result == 2);
}

使用zig test命令运行源代码中的测试,在标准库中,测试大多跟源代码处于同一文件。

Zig运行

类似于go run,Zig提供了zig run命令,将编译和运行源代码的步骤结合:

1
zig run my_program.zig

Defer

Zig与Go一样,通过defer概念来管理退出堆栈,当作用域块结束时执行清理操作等。

1
2
3
4
5
6
7
8
9
10
const print = @import("std").debug.print;

fn addSingleDigits(a: u32, b: u32) !u32 {
defer print("this is deferred!");

if (a > 9) return error.TooLarge;
if (b > 9) return error.TooLarge;

return a + b;
}

Comptime

comptime是Zig的一项有趣特性。Zig没有单独的宏系统,而是通过comptime将其代码编写的灵活性扩展到编译阶段。

comptime允许在编译时进行如下操作:

  • 在编译时解析变量和表达式
  • 根据编译时值行为的函数
  • 编译期间有选择性地执行comptime代码块
  • 编译时执行的元编程

泛型

在 Zig 中,comptime 提供了对类型值的访问,可以像普通数据值一样存储和传递这些类型值。

这使得可以创建带有类型参数的函数,如下所示:

1
2
3
4
5
6
7
8
9
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}

test "max with different types" {
const condition = false;
const result = max(if (condition) f32 else u64, 1234, 5678);
_ = result;
}

由于 comptime 类型值被视为普通类型,Zig 允许使用它们来构建泛型数据结构。例如,MakeList 使用 comptime 类型信息来返回一个编译时结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const std = @import("std");

fn MakeList(comptime T: type, comptime size: usize) type {
return struct {
items: [size]T,
};
}

pub fn main() void {
var list = MakeList(i32, 3){
.items = [3]i32{1, 2, 3},
};
std.debug.print("List {}\n", .{list});
}

在这个示例中,MakeList 函数使用 comptime 类型和大小参数,返回一个包含固定数量元素的结构体。

Zig 编译

  1. Zig作为 C (交叉) 编译器

Zig 工具链包含完整的 C 编译器,因此可以使用 Zig 来替换当前的 C 编译器工具链。以下是 hello.c 的源代码文件:

1
2
3
4
5
6
#include <stdio.h>

int main(int argc, char **argv) {
printf("Hello world\n");
return 0;
}

使用以下命令,Zig 可以将该源代码编译成可执行的二进制文件:

1
zig build-exe hello.c --library c
  1. Zig 和 C 交叉编译

Zig 让交叉编译(无论是 C 代码还是 Zig 代码亦或Rust)变得简单。无需繁琐的“自行准备交叉编译工具链”。Zig 提供所有必要的工具和库,确保您可以面向其支持的任何架构进行编译。

例如,Zig 可以将上述 C 源代码交叉编译成一个面向 Linux 的静态二进制文件(使用 musl 库):

1
zig build-exe hello.c --library c -target x86_64-linux-musl
  1. Zig 和 CGo 交叉编译

Zig 对 C 的交叉编译对在交叉编译启用了 CGo 的 Go 源代码时特别有用。例如, add.c 中的C 函数 add

1
2
3
4
5
#include <stdint.h>

int32_t add(int32_t a, int32_t b) {
return a + b;
}

我们可以在Go中 调用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

/*
#include "add.c"
*/
import "C"
import (
"fmt"
)

func main() {
a, b := int32(3), int32(4)
result := C.add(a, b)
fmt.Printf("%d + %d = %d\n", a, b, result)
}

假设我们在 MacOS 上构建代码,可以使用命令 zig cc 来使用 Zig 的 C 编译器,将 C 代码交叉编译成目标文件并与 Go 的目标文件链接,以构建适用于 x86 架构 Linux 的静态二进制文件:

1
CGO_ENABLED=1 GOOS=linux CC="zig cc -target x86_64-linux-musl" go build .

要让这一步成功,您只需安装 Zig 工具链,无其他依赖项!

总结

这篇博客为您简单介绍了 Zig 的功能。Zig融合了简洁性、强大性、安全性和对 C 的兼容性,为开发者提供了一个令人兴奋的选择。无论您是为新项目寻找语言,还是像我一样想扩展编程技能,Zig 都是一个值得探索的创新选择。

译文链接