The Goose Programming Language
本文为原文前 6,000 字符的节选翻译,完整内容请查看原文。
The Goose Programming Language. A memory-safe systems language with no heap allocator, garbage collector, or lifetime annotations. In this repository’s benchmarks, Goose runs faster than C++ and Rust while using less memory. Tutorial · Reference · Specification · Packages · Samples · Benchmarks · Standard library.
Goose 编程语言。一种内存安全的系统语言,没有堆分配器、垃圾回收器或生命周期注解。在该仓库的基准测试中,Goose 的运行速度比 C++ 和 Rust 更快,同时占用的内存更少。教程 · 参考 · 规范 · 包 · 示例 · 基准测试 · 标准库。
Goose has syntax familiar to C and Rust programmers, but there is no heap. Every dynamic value lives inline on data stacks managed by the compiler. Growth advances a pointer, and memory is freed when its scope ends. The language’s type and lifetime rules make this memory model safe to use.
Goose 的语法对于 C 和 Rust 程序员来说很熟悉,但它没有堆。每个动态值都内联存在于由编译器管理的数据栈上。增长会推进指针,内存会在作用域结束时释放。该语言的类型和生命周期规则使得这种内存模型可以安全使用。
Why Goose: Faster than C++ and than safe Rust, while memory safe. Over sixteen benchmarks Goose runs at 3.3x the speed of idiomatic C++, 1.16x hand-optimized C++ and 1.12x the best safe Rust, and uses 1.9x, 1.3x and 1.2x less memory (results). These gains come from Goose’s data layouts and memory model.
为什么选择 Goose:比 C++ 和安全的 Rust 更快,同时保持内存安全。在超过十六项基准测试中,Goose 的运行速度是惯用 C++ 的 3.3 倍,是手工优化 C++ 的 1.16 倍,是最佳安全 Rust 的 1.12 倍,并且内存占用分别减少了 1.9 倍、1.3 倍和 1.2 倍(结果)。这些提升源于 Goose 的数据布局和内存模型。
No allocator, no GC, no reference counting, no destructors. Memory is a handful of data stacks that the compiler assigns statically. Freeing a structure with a million elements takes a single store, no matter how deeply it is nested. Growth preserves references. A reference into a growing array stays valid for as long as the array does.
没有分配器,没有 GC,没有引用计数,没有析构函数。内存是编译器静态分配的少数几个数据栈。释放一个包含一百万个元素的结构体只需一次存储操作,无论它嵌套得有多深。增长会保留引用。指向增长数组的引用只要数组存在就一直有效。
In the corresponding benchmarks, Goose keeps typed references where C++ must call reserve and safe Rust uses u32 indices. Memory safe with zero annotations. There is no lifetime syntax, there are no aliasing or exclusivity rules, and there is no unsafe. The compiler infers what every reference is rooted at and rejects references that could outlive their owners.
在相应的基准测试中,Goose 保留了类型化引用,而 C++ 必须调用 reserve,安全的 Rust 则使用 u32 索引。零注解的内存安全。没有生命周期语法,没有别名或排他性规则,也没有 unsafe。编译器会推断每个引用的根源,并拒绝那些可能比其所有者存活时间更长的引用。
Nested data stays inline. A string, an array of strings, a record with variable-size fields and an array of those records are each stored as one contiguous block that contains no pointers. A example record that takes 160 bytes and one allocation in C++ takes 29 bytes and no allocations in Goose.
嵌套数据保持内联。字符串、字符串数组、带有可变大小字段的记录以及这些记录的数组,每个都存储为一个不包含指针的连续块。一个在 C++ 中占用 160 字节和一次分配的示例记录,在 Goose 中仅占用 29 字节且无需分配。
Variable sized Enums. Variable-mode ADTs store each value at the size of its own variant rather than that of the largest variant. On the benchmark that exercises this, they use 4x less memory than a Rust enum and run at 2x its speed.
可变大小的枚举。可变模式的代数数据类型(ADT)按其自身变体的大小存储每个值,而不是按最大变体的大小存储。在测试此功能的基准测试中,它们的内存占用比 Rust 枚举少 4 倍,运行速度是其 2 倍。
Links narrower than pointers. A relative reference stores a typed, checked link as a 1, 2 or 4-byte offset. Structures built from them are position independent, so a data structure can be written to a file as is. Saving it is a single write, and loading it is a single read followed by a verification pass that rejects malformed or hostile data.
比指针更窄的链接。相对引用将类型化、经过检查的链接存储为 1、2 或 4 字节的偏移量。由它们构建的结构是位置无关的,因此数据结构可以直接写入文件。保存它只需一次写入,加载它只需一次读取,随后进行验证以拒绝格式错误或恶意数据。
Everything is built in place, guaranteed. A value is constructed at its final destination through any depth of calls. items.push(parse(line)) writes the parsed record directly into the array, and returning a growable array by value does not copy it. Returns across multiple calls. return err from load returns from a function any number of frames up the call stack. It is checked statically, and needs no unwinder, no Result type and no ? on every call.
一切都在原地构建,有保证。值通过任意深度的调用在其最终目的地被构造。items.push(parse(line)) 将解析后的记录直接写入数组,按值返回可增长数组时不会进行复制。跨多个调用的返回。从 load 返回 err 可以从函数返回调用栈上任意数量的帧。它是静态检查的,不需要展开器,不需要 Result 类型,也不需要在每次调用时使用 ?。
Threads with no shared memory. A worker is compiled as a separate program with its own memory, and flat values are passed between threads through typed queues with a memcpy. Data races, locks, atomics and memory orderings do not exist in the language.
没有共享内存的线程。工作线程被编译为具有独立内存的单独程序,扁平值通过类型化队列和内存拷贝(memcpy)在线程间传递。语言中不存在数据竞争、锁、原子操作和内存排序。
Generics and higher-order functions with no overhead. An untyped parameter is generic. Function values are compile-time entities, so xs.filter() { it > 0 } compiles to what the handwrittend loop would looks like and builds its result directly in its destination.
没有开销的泛型和高阶函数。未类型化的参数是泛型的。函数值是编译时实体,因此 xs.filter() { it > 0 } 会编译成手写循环的样子,并直接在其目的地构建结果。
Compiles to C and can call C. Goose compiles to one C file, so it runs wherever a C compiler does and calls C directly through extern fn. The bundled TinyCC backend compiles and runs a program in-process, with no build step. The tutorial walks through all of this by example, the reference covers the whole language topic by topic, the specification has the exact rules, and the benchmarks have the numbers, including the cases where Goose loses.
编译为 C 并可以调用 C。Goose 编译为一个 C 文件,因此它可以在任何有 C 编译器的地方运行,并通过 extern fn 直接调用 C。捆绑的 TinyCC 后端可以在进程内编译并运行程序,无需构建步骤。教程通过示例讲解了所有这些内容,参考手册按主题涵盖了整个语言,规范包含确切的规则,基准测试提供了数据,包括 Goose 表现不佳的情况。
Features: This section focuses on what distinguishes Goose. The tutorial explains these features in more detail, and the samples show them in thirty-one complete programs. One memory model: data stacks freed at scope exit. A program has the native call stack, static data and N data stacks, where the compiler works out N.
特性:本节重点介绍 Goose 的独特之处。教程更详细地解释了这些特性,示例在三十一个完整程序中展示了它们。一种内存模型:在作用域结束时释放数据栈。程序拥有原生调用栈、静态数据和 N 个数据栈,其中 N 由编译器计算得出。
A data stack is a large address-space reservation with a bump pointer, and there is no other memory. At most one resizable value is live per stack and it is always on top, so growth never moves anything and never checks a capacity. All of this is proved at compile time; the runtime tracks only the bump pointers.
数据栈是一个带有碰撞指针的大型地址空间预留,没有其他内存。每个栈最多有一个可调整大小的值处于活动状态,且它始终位于顶部,因此增长永远不会移动任何东西,也从不检查容量。所有这些都在编译时得到证明;运行时仅跟踪碰撞指针。
Memory never moves, so references survive growth. push returns a reference to the new element, so a program can link elements while building the array. A vector
内存从不移动,因此引用在增长后依然有效。push 返回对新元素的引用,因此程序可以在构建数组时链接元素。vector
Safe references, no annotations. Every reference and slice has a static root: the variable that bounds its target’s lifetime. A reference must not outlive the variable that owns its target or access that target through an incompatible type. The compiler infers roots and specializes functions for them, so no annotations are needed. There are no aliasing or exclusivity rules.
安全的引用,无需注解。每个引用和切片都有一个静态根:即限制其目标生命周期的变量。引用不得比拥有其目标的变量存活时间更长,也不得通过不兼容的类型访问该目标。编译器会推断根并为其专门化函数,因此无需注解。没有别名或排他性规则。