Zig And Rust (双语)

作者 : Iseri Nina

This post will be a bit all over the place. Several months ago, I wrote Hard Mode Rust, exploring an allocation-conscious style of programming. In the ensuing discussion, @jamii name-dropped TigerBeetle, a reliable, distributed, fast, and small database written in Zig in a similar style, and, well, I now find myself writing Zig full-time, after more than seven years of Rust. This post is a hand-wavy answer to the “why?” question. It is emphatically not a balanced and thorough comparison of the two languages. I haven’t yet written my 100k lines of Zig to do that. (if you are looking for a more general “what the heck is Zig”, I can recommend @jamii’s post). In fact, this post is going to be less about languages, and more about styles of writing software (but pre-existing knowledge of Rust and Zig would be very helpful). Without further caveats, let’s get started.

这篇文章会有点杂乱无章。几个月前,我写了Hard Mode Rust,探讨了一种注重内存分配的编程风格。在随后的讨论中,@jamii提到了TigerBeetle,这是一种可靠、分布式、快速且小巧的数据库,用Zig编写,风格相似。结果,我现在全职写Zig,已经超过七年的Rust经验。这篇文章是对“为什么?”这个问题的一个笼统回答。它绝不是对这两种语言的平衡和全面的比较。我还没有写过100k行的Zig来做到这一点。(如果你在寻找更一般的“Zig是什么”,我可以推荐@jamii的文章)。事实上,这篇文章将更少关于语言,而更多关于软件编写风格(但已有的Rust和Zig知识会非常有帮助)。不再多说,让我们开始吧。

Reliable Software 可靠的软件

To the first approximation, we all strive to write bug-free programs. But I think a closer look reveals that we don’t actually care about programs being correct 100% of the time, at least in the majority of the domains. Empirically, almost every program has bugs, and yet it somehow works out OK. To pick one specific example, most programs use stack, but almost no programs understand what their stack usage is exactly, and how far they can go. When we call malloc, we just hope that we have enough stack space for it, we almost never check. Similarly, all Rust programs abort on OOM, and can’t state their memory requirements up-front. Certainly good enough, but not perfect.
从第一个近似来看,我们都努力编写无错误的程序。但我认为仔细观察会发现,我们实际上并不关心程序在所有时间都是100%正确的,至少在大多数领域是这样。经验上,几乎每个程序都有错误,但它们却以某种方式正常运行。举一个具体的例子,大多数程序使用堆栈,但几乎没有程序确切了解其堆栈使用情况,以及能走多远。当我们调用malloc时,我们只是希望有足够的堆栈空间,几乎从不检查。同样,所有Rust程序在OOM时都会中止,无法提前说明其内存需求。虽然足够好,但并不完美。

The second approximation is that we strive to balance program usefulness with the effort to develop the program. Bugs reduce usefulness a lot, and there are two styles of software engineering to deal with the:
第二个近似是我们努力在程序的有用性和开发程序的努力之间取得平衡。错误大大降低了有用性,有两种软件工程风格来处理:

Erlang style, where we embrace failability of both hardware and software and explicitly design programs to be resilient to partial faults.
Erlang风格,我们接受硬件和软件的可失败性,并明确设计程序以对部分故障具有弹性。

SQLitestyle, where we overcome an unreliable environment at the cost of rigorous engineering.
SQLite风格,我们以严格的工程为代价克服不可靠的环境。

rust-analyzer and TigerBeetle are perfect specimens of the two approaches, let me describe them.
rust-analyzer和TigerBeetle是这两种方法的完美例子,让我来描述它们。

rust-analyzer

rust-analyzer is an LSP server for the Rust programming language. By its nature, it’s expansive. Great developer tools usually have a feature for every niche use-case. It also is a fast-moving open source project which has to play catch-up with the rustc compiler. Finally, the nature of IDE dev tooling makes availability significantly more important than correctness. An erroneous completion option would cause a smirk (if it is noticed at all), while the server crashing and all syntax highlighting turning off will be noticed immediately.

rust-analyzer是Rust编程语言的LSP服务器。其本质是广泛的。优秀的开发者工具通常为每个小众用例提供功能。它也是一个快速发展的开源项目,必须与rustc编译器保持同步。最后,IDE开发工具的性质使得可用性比正确性更为重要。一个错误的补全选项可能会引起一笑(如果被注意到的话),而服务器崩溃和所有语法高亮关闭将立即被注意到。

For this cluster of reasons, rust-analyzer is shifted far towards the “embrace software imperfections” side of the spectrum. rust-analyzer is designed around having bugs. All the various features are carefully compartmentalized at runtime, such that panicking code in just a single feature can’t bring down the whole process. Critically, almost no code has access to any mutable state, so usage of catch_unwind can’t lead to a rotten state.

由于这一系列原因,rust-analyzer 在光谱上大大偏向于“接受软件缺陷”这一侧。rust-analyzer 的设计是围绕着存在缺陷而进行的。所有各种功能在运行时都被仔细地隔离开来,以至于单个功能中的恐慌代码不会导致整个进程崩溃。关键是,几乎没有代码可以访问任何可变状态,因此使用 catch_unwind 不会导致状态腐败。

Development process itself is informed by this calculus. For example, PRs with new features land when there’s a reasonable certainty that the happy case works correctly. If some weird incomplete code would cause the feature to crash, that’s OK. It might be even a benefit — fixing a well-reproducible bug in an isolated feature is a gateway drug to heavy contribution to rust-analyzer. Our tight weekly release schedule (and the nightly release) help to get bug fixes out there faster.

开发过程本身也受到这种计算的影响。例如,当有合理的确定性认为正常情况可以正确工作时,带有新功能的 PR 就会被合并。如果某些奇怪的不完整代码会导致功能崩溃,那也没关系。这甚至可能是一个好处——在一个独立的功能中修复一个易于重现的错误是对 rust-analyzer 进行大量贡献的入门药物。我们紧凑的每周发布计划(以及每晚的发布)有助于更快地推出错误修复。

Overall, the philosophy is to maximize provided value by focusing on the common case. Edge cases become eventually correct over time.

总体而言,这种理念是通过关注常见情况来最大化提供的价值。边缘情况随着时间的推移会逐渐变得正确。

TigerBeetle

TigerBeetle is the opposite of that.

TigerBeetle 则与此相反。

It is a database, with domain model fixed at compile time (we currently do double-entry bookkeeping). The database is distributed, meaning that there are six TigerBeetle replicas running on different geographically and operationally isolated machines, which together implement a replicated state machine. That is, TigerBeetle replicas exchange messages to make sure every replica processes the same set of transactions, in the same order. That’s a surprisingly hard problem if you allow machines to fail (the whole point of using many machines for redundancy), so we use a smart consensus algorithm (non-byzantine) for this. Traditionally, consensus algorithms assume reliable storage — data once written to disk can be always retrieved later. In reality, storage is unreliable, nearly byzantine — a disk can return bogus data without signaling an error, and even a single such error can break consensus. TigerBeetle combats that by allowing a replica to repair its local storage using data from other replicas.

它是一个数据库,其领域模型在编译时固定(我们目前进行复式记账)。该数据库是分布式的,这意味着有六个 TigerBeetle 副本运行在不同的地理和操作上隔离的机器上,它们共同实现一个复制状态机。也就是说,TigerBeetle 副本交换消息以确保每个副本以相同的顺序处理相同的事务。如果允许机器故障(使用多台机器进行冗余的全部意义),这将是一个非常困难的问题,因此我们为此使用了一个智能的共识算法(非拜占庭)。传统上,共识算法假设存储可靠——一旦数据写入磁盘,就可以始终检索到。实际上,存储是不可靠的,几乎是拜占庭式的——磁盘可以返回错误数据而不发出错误信号,甚至单个这样的错误就可以破坏共识。TigerBeetle 通过允许副本使用其他副本的数据修复其本地存储来应对这一问题。

On the engineering side of things, we are building a reliable, predictable system. And predictable means really predictable. Rather than reining in sources of non-determinism, we build the whole system from the ground up from a set of fully deterministic, hand crafted components. Here are some of our unconventional choices (design doc):

在工程方面,我们正在构建一个可靠且可预测的系统。可预测意味着真正的可预测。我们不是抑制非确定性来源,而是从头开始用一套完全确定性的、手工制作的组件构建整个系统。以下是我们的一些非常规选择(设计文档):

It’s hard mode! We allocate all the memory at a startup, and there’s zero allocation after that. This removes all the uncertainty about allocation.

这是困难模式!我们在启动时分配所有内存,之后不再进行任何分配。这消除了所有关于分配的不确定性。

The code is architected with brutal simplicity. As a single example, we don’t use JSON, or ProtoBuf, or Cap’n’Proto for serialization. Rather, we just cast the bytes we received from the network to a desired type. The motivation here is not so much performance, as reduction of the number of moving parts. Parsing is hard, but, if you control both sides of the communication channel, you don’t need to do it, you can send checksummed data as is.

代码的架构极其简单。举一个简单的例子,我们不使用JSON、ProtoBuf或Cap’n’Proto进行序列化。相反,我们只是将从网络接收到的字节转换为所需的类型。这里的动机与其说是性能,不如说是减少活动部件的数量。解析是困难的,但如果你控制通信通道的两端,你就不需要这样做,你可以按原样发送带有校验和的数据。

We aggressively minimize all dependencies. We know exactly the system calls our system is making, because all IO is our own code (on Linux, our main production platform, we don’t link libc).

我们积极地最小化所有依赖项。我们确切地知道我们的系统正在进行哪些系统调用,因为所有的IO都是我们自己的代码(在Linux上,我们的主要生产平台,我们不链接libc)。

There’s little abstraction between components — all parts of TigerBeetle work in concert. For example, one of our core types, Message, is used throughout the stack:

组件之间几乎没有抽象——TigerBeetle的所有部分都协同工作。例如,我们的核心类型之一,Message,在整个堆栈中使用:

  • network receives bytes from a TCP connection directly into a Message
    网络直接从TCP连接接收字节到Message
  • consensus processes and sends Messages
    共识处理并发送Message
  • similarly, storage writes Messages to disk
    同样,存储将Message写入磁盘

This naturally leads to very simple and fast code. We don’t need to do anything special to be zero copy — given that we allocate everything up-front, we simply don’t have any extra memory to copy the data to! (A separate issue is that, arguably, you just can’t treat storage as a separate black box in a fault-tolerant distributed system, because storage is also faulty).
这自然导致代码非常简单和快速。我们不需要做任何特别的事情来实现零拷贝——因为我们预先分配了所有内容,我们根本没有多余的内存来复制数据!(另一个问题是,可以说,在容错分布式系统中,你不能将存储视为一个独立的黑盒,因为存储也是有故障的)。

Everything in TigerBeetle has an explicit upper-bound. There’s not a thing which is just an u32 — all data is checked to meet specific numeric limits at the edges of the system.

在TigerBeetle中,一切都有明确的上限。没有什么是仅仅一个u32——所有数据都在系统边缘被检查以满足特定的数值限制。

This includes Messages. We just upper-bound how many messages can be in-memory at the same time, and allocate precisely that amount of messages (source). Getting a new message from the message pool can’t allocate and can’t fail.

这包括Messages。我们只是对内存中可以同时存在的消息数量进行上限,并精确分配该数量的消息(来源)。从消息池中获取新消息不能分配也不能失败。

With all that strictness and explicitness about resources, of course we also fully externalize any IO, including time. All inputs are passed in explicitly, there’s no ambient influences from the environment. And that means that the bulk of our testing consists of trying all possible permutations of effects of the environment. Deterministic randomized simulation is very effective at uncovering issues in real implementations of distributed systems.

由于对资源的严格性和明确性,我们当然也完全外部化了任何IO,包括时间。所有输入都是显式传入的,没有来自环境的影响。这意味着我们的大部分测试都在尝试环境影响的所有可能排列。确定性的随机模拟在揭示分布式系统实际实现中的问题方面非常有效

What I am getting at is that TigerBeetle isn’t really a normal “program” program. It strictly is a finite state machine, explicitly coded as such.

我想说的是,TigerBeetle并不是真正意义上的普通“程序”程序。它严格来说是一个有限状态机,明确地以这种方式编码。

Back From The Weeds 从细节中回归

Oh, right, Rust and Zig, the topic of the post!

哦,对了,Rust和Zig,这是文章的主题!

I find myself often returning to the first Rust slide deck. A lot of core things are different (no longer Rust uses only the old ideas), but a lot is the same. To be a bit snarky, while Rust “is not for lone genius hackers”, Zig … kinda is. On more peaceable terms, while Rust is a language for building modular software, Zig is in some sense anti-modular.

我经常回到第一个Rust幻灯片。许多核心内容不同(Rust不再仅仅使用旧的想法),但很多仍然相同。稍微讽刺一点,虽然Rust“不是为孤独的天才黑客准备的”,但Zig……有点是。更和平地说,虽然Rust是一种用于构建模块化软件的语言,但在某种意义上,Zig是反模块化的。

It’s appropriate to quote Bryan Cantrill here:

引用Bryan Cantrill的话是合适的:

I can write C that frees memory properly…that basically doesn’t suffer from memory corruption…I can do that, because I’m controlling heaven and earth in my software. It makes it very hard to compose software. Because even if you and I both know how to write memory safe C, it’s very hard for us to have an interface boundary where we can agree about who does what.

我可以写出正确释放内存的C代码……基本上不会遭受内存损坏……我可以做到,因为我在我的软件中控制着一切。这使得软件组合非常困难。因为即使你和我都知道如何编写内存安全的C代码,我们也很难在接口边界上达成一致,谁做什么。

That’s the core of what Rust is doing: it provides you with a language to precisely express the contracts between components, such that components can be integrated in a machine-checkable way.
这就是Rust的核心:它为你提供了一种语言,可以精确表达组件之间的契约,以便组件可以以机器可检查的方式集成。

Zig doesn’t do that. It isn’t even memory safe. My first experience writing a non-trivial Zig program went like this:
Zig不这样做。它甚至不是内存安全的。我第一次编写一个非平凡的Zig程序时是这样的:

ME: Oh wow! Do you mean I can finally just store a pointer to a struct’s field in the struct itself?

我:哦,哇!你的意思是我终于可以仅仅在结构体本身中存储一个指向结构体字段的指针了吗?

30 seconds later

30秒后

PROGRAM: Segmentation fault.
程序:分段错误。

However!

然而!

Zig is a much smaller language than Rust. Although you’ll have to be able to keep the entirety of the program in your head, to control heaven and earth to not mess up resource management, doing that could be easier.
Zig 一种比Rust小得多的语言。虽然你需要能够将整个程序都记在脑海中,以控制天地不搞砸资源管理,但这样做可能会更容易。

It’s not true that rewriting a Rust program in Zig would make it simpler. On the contrary, I expect the result to be significantly more complex (and segfaulty). I noticed that a lot of Zig code written in “let’s replace RAII with defer” style has resource-management bugs.

将Rust程序重写为Zig并不会使其更简单。相反,我预计结果会显著更复杂(并且更容易出现段错误)。我注意到,许多以“让我们用defer替换RAII”风格编写的Zig代码存在资源管理错误。

But it often is possible to architect the software such that there’s little resource management to do (eg, allocating everything up-front, like TigerBeetle, or even at compile time, like many smaller embedded systems). It’s hard — simplicity is always hard. But, if you go this way, I feel like Zig can provide substantial benefits.

但是,通常可以设计软件,使得几乎不需要进行资源管理(例如,像TigerBeetle那样预先分配所有资源,或者像许多较小的嵌入式系统那样在编译时分配)。这很难——简单总是很难。但是,如果你选择这种方式,我觉得Zig可以提供实质性的好处。

Zig has just a single feature, dynamically-typed comptime, which subsumes most of the special-cased Rust machinery. It is definitely a tradeoff, instantiation-time errors are much worse for complex cases. But a lot more of the cases are simple, because there’s no need for programming in the language of types. Zig is very spartan when it comes to the language. There are no closures — if you want them, you’ll have to pack a wide-pointer yourself. Zig’s expressiveness is aimed at producing just the right assembly, not at allowing maximally concise and abstract source code. In the words of Andrew Kelley, Zig is a DSL for emitting machine code.

Zig 只有一个功能,即动态类型的编译时(comptime),它涵盖了大多数特殊情况的 Rust 机制。这无疑是一种权衡,对于复杂情况,实例化时的错误要糟糕得多。但很多情况下更简单,因为不需要在类型语言中编程。Zig 在语言方面非常简朴。没有闭包——如果你需要它们,你将不得不自己打包一个宽指针。Zig 的表达能力旨在生成恰到好处的汇编代码,而不是允许最大限度简洁和抽象的源代码。用 Andrew Kelley 的话来说,Zig 是一种用于生成机器代码的 DSL。

Zig strongly prefers explicit resource management. A lot of Rust programs are web-servers. Most web servers have a very specific execution pattern of processing multiple independent short-lived requests concurrently. The most natural way to code this would be to give each request a dedicated bump allocator, which turns drops into no-ops and “frees” the memory at bulk after each request by resetting offset to zero. This would be pretty efficient, and would provide per-request memory profiling and limiting out of the box. I don’t think any popular Rust frameworks do this — using the global allocator is convenient enough and creates a strong local optima. Zig forces you to pass the allocator in, so you might as well think about the most appropriate one!

Zig 强烈偏好显式资源管理。许多 Rust 程序是网络服务器。大多数网络服务器具有非常特定的执行模式,即同时处理多个独立的短期请求。最自然的编码方式是为每个请求提供一个专用的 bump 分配器,这将使释放操作变为无操作,并在每个请求后通过将偏移量重置为零来批量“释放”内存。这将是相当高效的,并且可以开箱即用地提供每个请求的内存分析和限制。我认为没有任何流行的 Rust 框架这样做——使用全局分配器已经足够方便,并创造了一个强大的局部最优。Zig 强制你传入分配器,因此你不妨考虑最合适的分配器!

Similarly, the standard library is very conscious about allocation, more so than Rust’s. Collections are not parametrized by an allocator, like in C++ or (future) Rust. Rather, an allocator is passed in explicitly to every method which actually needs to allocate. This is Call Site Dependency Injection, and it is more flexible. For example in TigerBeetle we need a couple of hash maps. These maps are sized at a startup time to hold just the right number of elements, and are never resized. So we pass an allocator to init method, but we don’t pass it to the event loop. We get to both use the standard hash-map, and to feel confident that there’s no way we can allocate in the actual event loop, because it doesn’t have access to an allocator.
同样,标准库在分配方面非常谨慎,比Rust更甚。集合像C++或(未来的)Rust那样由分配器参数化。相反,分配器被显式传递给每个实际需要分配的方法。这是调用点依赖注入,它更灵活。例如,在TigerBeetle中,我们需要几个哈希映射。这些映射在启动时被调整到恰好容纳正确数量的元素,并且从不调整大小。因此,我们将分配器传递给init方法,但不传递给事件循环。我们既可以使用标准哈希映射,又可以确信在实际事件循环中无法进行分配,因为它无法访问分配器。

Wishlist 愿望清单

Finally, my wishlist for Zig.

最后,我对Zig的愿望清单。

First, I think Zig’s strength lies strictly in the realm of writing “perfect” systems software. It is a relatively thin slice of the market, but it is important. One of the problems with Rust is that we don’t have a reliability-oriented high-level programming language with a good quality of implementation (modern ML, if you will). This is a blessing for Rust, because it makes its niche bigger, increasing the amount of community momentum behind the language. This is also a curse, because a bigger niche makes it harder to maintain focus. For Zig, Rust already plays this role of “modern ML”, which creates bigger pressure to specialize.

首先,我认为Zig的强项严格在于编写“完美”系统软件的领域。这是市场中相对较小的一部分,但很重要。Rust的一个问题是我们没有一个以可靠性为导向的高水平编程语言,具有良好的实现质量(如果你愿意,可以称之为现代ML)。这对Rust来说是一个福音,因为它使其利基市场更大,增加了语言背后的社区动力。这也是一个诅咒,因为更大的利基市场使得保持专注更加困难。对于Zig来说,Rust已经扮演了“现代ML”的角色,这给专业化带来了更大的压力。

Second, my biggest worry about Zig is its semantics around aliasing, provenance, mutability and self-reference ball of problems. I don’t worry all that much about this creating “iterator invalidation” style of UB. TigerBeetle runs in -DReleaseSafe, which mostly solves spatial memory safety, it doesn’t really do dynamic memory allocation, which unasks the question about temporal memory safety, and it has a very thorough fuzzer-driven test suite, which squashes the remaining bugs. I do worry about the semantics of the language itself. My current understanding is that, to correctly compile a C-like low-level language, one really needs to nail down semantics of pointers. I am not sure “portable assembly” is really a thing: it is possible to create a compiler which does little optimization and “works as expected” most of the time, but I am doubtful that it’s possible to correctly describe the behavior of such a compiler. If you start asking questions about what are pointers, and what is memory, you end up in a fairly complicated land, where bytes are poison. Rust tries to define that precisely, but writing code which abides by the Rust rules without a borrow-checker isn’t really possible — the rules are too subtle. Zig’s implementation today is very fuzzy around potentially aliased pointers, copies of structs with interior-pointers and the like. I wish that Zig had a clear answer to what the desired semantics is.

其次,我对Zig最大的担忧是其在别名、来源、可变性和自引用问题上的语义。我并不太担心这会产生“迭代器失效”类型的未定义行为。TigerBeetle运行在-DReleaseSafe,这主要解决了空间内存安全问题,它实际上不进行动态内存分配,这就不再需要考虑时间内存安全问题,并且它有一个非常彻底的基于模糊测试的测试套件,可以消除剩余的错误。我确实担心语言本身的语义。根据我目前的理解,要正确编译类似C的低级语言,确实需要明确指针的语义。我不确定“可移植汇编”是否真的存在:可以创建一个优化很少且“在大多数情况下按预期工作”的编译器,但我怀疑是否有可能正确描述这种编译器的行为。如果你开始询问指针是什么,内存是什么,你会进入一个相当复杂的领域,字节是有害的。Rust试图精确定义这一点,但在没有借用检查器的情况下编写符合Rust规则的代码几乎是不可能的——规则过于微妙。Zig的当前实现对潜在别名指针、具有内部指针的结构体副本等问题的处理非常模糊。我希望Zig能对期望的语义有一个明确的答案。

Third, IDE support. I’ve written about that before on this blog. As of today, developing Zig is quite pleasant — the language server is pretty spartan, but already is quite helpful, and for the rest, Zig is exceptionally greppable. But, with the lazy compilation model and the absence of out-of-the-language meta programming, I feel like Zig could be more ambitious here. To position itself well for the future in terms of IDE support, I think it would be nice if the compiler gets the basic data model for IDE use-case. That is, there should be an API to create a persistent analyzer process, which ingests a stream of code edits, and produces a continuously updated model of the code without explicit compilation requests. The model can be very simple, just “give me an AST of this file at this point in time” would do — all the fancy IDE features can be filled in later. What matters is a shape of data flow through the compiler — not an edit-compile cycle, but rather a continuously updated view of the world.

第三,IDE 支持。我之前在这个博客上写过。到今天为止,开发 Zig 是相当愉快的——语言服务器相当简朴,但已经很有帮助,而在其他方面,Zig 的可搜索性极佳。但是,考虑到惰性编译模型和缺乏语言外的元编程,我觉得 Zig 在这方面可以更有野心。为了在 IDE 支持方面为未来做好准备,我认为如果编译器能为 IDE 用例提供基本的数据模型会很好。也就是说,应该有一个 API 来创建一个持久的分析器进程,它接收代码编辑流,并在没有显式编译请求的情况下生成代码的持续更新模型。模型可以非常简单,只需“在此时此刻给我这个文件的 AST”即可——所有花哨的 IDE 功能可以稍后填充。重要的是通过编译器的数据流形态——不是编辑-编译循环,而是世界的持续更新视图。

Fourth, one of the values of Zig which resonates with me a lot is a preference for low-dependency, self-contained processes. Ideally, you get yourself a ./zig binary, and go from there. The preference, at this time of changes, is to bundle a particular version of ./zig with a project, instead of using a system-wide zig. There are two aspects that could be better.
第四,Zig 的一个让我非常共鸣的价值观是对低依赖性、自包含进程的偏好。理想情况下,你可以得到一个 ./zig 二进制文件,然后从那里开始。在这个变化的时代,偏好是将特定版本的 ./zig 与项目捆绑在一起,而不是使用系统范围的 zig。有两个方面可以做得更好。

“Getting yourself a Zig” is a finicky problem, because it requires bootstrapping. To do that, you need to run some code that will download the binary for your platform, but each platform has its own way to “run code”. I wish that Zig provided a blessed set of scripts, get_zig.sh, get_zig.bat, etc (or maybe a small actually portable binary?), which projects could just vendor, so that the contribution experience becomes fully project-local and self-contained:
“获取一个 Zig”是一个棘手的问题,因为它需要引导。为此,你需要运行一些代码来下载适合你平台的二进制文件,但每个平台都有自己的“运行代码”方式。我希望 Zig 提供一套受认可的脚本,get_zig.shget_zig.bat,等等(或者也许是一个小的真正可移植的二进制文件?),项目可以直接使用,以便贡献体验完全项目本地化和自包含:

$ ./get_zig.sh
$ ./zig build

Once you have ./zig, you can use that to drive the rest of the automation. You already can ./zig build to drive the build, but there’s more to software than just building. There’s always a long tail of small things which traditionally get solved with a pile of platform-dependent bash scripts. I wish that Zig pushed the users harder towards specifying all that automation in Zig. A picture is worth a thousand words, so

一旦你有了 ./zig,你就可以用它来驱动其余的自动化。你已经可以用 ./zig build 来驱动构建,但软件不仅仅是构建。总是有一长串小事情,传统上通过一堆平台相关的 bash 脚本来解决。我希望 Zig 更加推动用户在 Zig 中指定所有这些自动化。图片胜过千言万语,所以

# BAD: dependency on the OS
$ ./scripts/deploy.sh --port 92

# OK: no dependency, but a mouthful to type
$ ./zig build task -- deploy --port 92

# Would be GREAT:
$ ./zig do deploy --port 92

Attempting to summarize,
尝试总结,

  • Rust is about compositional safety, it’s a more scalable languagethan Scala.
    Rust 关注组合安全性,它是一种比 Scala 更具可扩展性的语言。
  • Zig is about perfection. It is a very sharp, dangerous, but,ultimately, more flexible tool.
    Zig 追求完美。它是一种非常锋利、危险,但最终更灵活的工具。

Discussion on /r/Zig and /r/rust.

转载自 https://matklad.github.io/2023/03/26/zig-and-rust.html 使用GPT-4o模型翻译。


版權聲明: 本部落格所有文章除特別聲明外,均採用 CC BY 4.0 許可協議。轉載請註明來源 Iseri Nina !
  目錄