Lobsters - 02 Oct 2026

Page 2 of 4

backing integer yields an undefined bitpack.

@bitCast now also performs a safety check for invalid tag values if its

destination type is an enum.

Also adds a std.meta.BackingInt function to get the result

type of @backingInt.

Zig now requires empty enums to have noreturn as their backing integer because

they are uninstantiable.

Upgrade example:

--- a/lib/std/Build.zig

+++ b/lib/std/Build.zig

@@ -145,7 +145,7 @@ pub const Graph = struct {

pub fn addGeneratedFile(graph: *Graph, owner: *Step) Configuration.GeneratedFileIndex {

graph.generated_files.append(graph.arena, owner) catch @panic("OOM");

- return @enumFromInt(graph.generated_files.items.len - 1);

+ return @fromBackingInt(@intCast(graph.generated_files.items.len - 1));

}

pub fn dupeString(graph: *const Graph, bytes: []const u8) []const u8 {

@@ -2607,7 +2607,7 @@ pub const LazyPath = union(enum) {

.src_path, .cwd_relative, .relative, .dependency => {},

.generated => |gen| {

const graph = other_step.owner.graph;

- const generated_owner_step = graph.generated_files.items[@intFromEnum(gen.index)];

+ const generated_owner_step = graph.generated_files.items[@backingInt(gen.index)];

other_step.dependOn(generated_owner_step);

},

}

zig fmt automatically performs this upgrade.

@SpirvType

SPIR-V has a number of types, such as images and samplers, that have no equivalent in Zig's type

system. Previously, the only way to refer to one of them was through inline assembly, which made it

impossible to declare a texture or a storage buffer as an ordinary global variable. Zig 0.17.0

implements accepted proposal #35240,

adding the @SpirvType builtin alongside the other type-creating builtins:

- .sampler creates an OpTypeSampler.

- .image creates an OpTypeImage.

- .sampled_image creates an OpTypeSampledImage from an image

type whose usage is .sampled.

- .runtime_array creates an OpTypeRuntimeArray.

It supports indexing and exposes a len field just like the array type.

Using this builtin when not targeting SPIR-V is a compile error. The options are also validated against the target OS.

Array multiplication syntax (a ** b) has been removed in favor of

@splat.

Migration:

--- a/player/chromaprint.zig

+++ b/player/chromaprint.zig

@@ -35,7 +35,7 @@ pub const Chroma = struct {

const max_index = @min(window_size / 2, freqToIndex(max_freq));

const notes: [window_size]u8 = n: {

@setEvalBranchQuota(window_size);

- var result = [1]u8{0} ** window_size;

+ var result: [window_size]u8 = @splat(0);

for (min_index..max_index) |i| {

const freq = indexToFreq(i);

const octave = freqToOctave(freq);

@@ -123,7 +123,7 @@ const RollingIntegralImage = struct {

num_rows: u32,

pub const init: RollingIntegralImage = .{

- .data = [1]Float{0} ** data_size,

+ .data = @splat(0),

.num_rows = 0,

};

@divCeil

The new @divCeil builtin performs integer division rounded toward positive

infinity, complementing the existing @divTrunc, @divFloor,

and @divExact builtins.

As with the other division builtins, caller guarantees that denominator != 0 and

that result does not overflow.

No more std.math.divCeil(a, b) catch unreachable!

@hasDecl Returns true Only for Public Declarations

Previously, @hasDecl returned true for public declarations

and declarations in the same file. Now, the behavior is the same independently of which file

@hasDecl is in.

comptime Length Slices

Now allowed:

void{} Syntax Removed

void{} is no longer valid syntax. Use {} instead (#15213).

errdefer Capture Removed

The capture (|err|) is no longer allowed (#23734).

To migrate, split the function into two:

fn processOneTarget(job: Job) void {

- errdefer |err| std.debug.panic("panic: {s}", .{@errorName(err)});

+ processOneTargetInner(job) catch |err| std.debug.panic("panic: {s}", .{@errorName(err)});

+}

+fn processOneTargetInner(job: Job) !void {

const target = job.target;

i0 Removed

i0 is no longer an allowed primitive integer type.

This type was nonsensical, so does not have a direct alternative. However, any uses of it can almost certainly be transparently replaced with u0.

internal and link_once Global Linkage Removed

The internal and link_once tags of

std.lang.GlobalLinkage have been removed as they had unclear semantics and

incomplete support in codegen and linking (#36956). Any use of link_once is likely served by

weak, while the replacement for internal is to simply not

@export the symbol in the first place.

- Added f128 support for @exp and @exp2 based on "Table-Driven Implementation of the Exponential Function in IEEE

Floating-Point Arithmetic" by Ping Tak Peter Tang, adapted to work with 128-bit numbers (#31846).

- Added std.Io.Semaphore.waitTimeout (#31924).

- Added std.spirv helpers for sampling, querying, and writing images

(#36187).

- ArrayHashMap.setKey no longer recomputes the entire index (#32136).

- std.Target.parseCpuModel now returns optional rather than error.

- std.debug.Pdb: deduplicate inline source locations (#35438).

- hash.crc full namespace audit (#35952).

- std.fs.path add appending variants for relative and resolve (#36784).

- std.heap.memory_pool.AlignedManaged removed in favor of std.heap.memory_pool.Aligned.

- std.heap.memory_pool.ExtraManaged removed in favor of std.heap.memory_pool.Extra.

- Deprecated std.builtin in favor of std.lang.

- Deprecated std.meta.fieldInfo in favor of @typeInfo.

- Deprecated std.meta.fieldNames in favor of @typeInfo.

- Deprecated std.meta.fieldTypes in favor of @typeInfo.

- Deprecated std.DoublyLinkedList.pop in favor of std.DoublyLinkedList.popLast.

- Renamed std.gpu to std.spirv.

- Removed std.ascii.indexOfIgnoreCase in favor of std.ascii.findIgnoreCase.

- Removed std.ascii.indexOfIgnoreCasePos in favor of std.ascii.findIgnoreCasePos.

- Removed std.ascii.indexOfIgnoreCasePosLinear in favor of std.ascii.findIgnoreCasePosLinear.

- Removed std.bit_set.Integer.initEmpty in favor of std.bit_set.Integer.empty.

- Removed std.bit_set.Integer.initFull in favor of std.bit_set.Integer.full.

- Removed std.bit_set.Array.initEmpty in favor of std.bit_set.Array.empty.

- Removed std.bit_set.Array.initFull in favor of std.bit_set.Array.full.

- Removed std.enums.EnumSet.initEmpty in favor of std.enums.EnumSet.empty.

- Removed std.enums.EnumSet.initFull in favor of std.enums.EnumSet.full.

- Removed std.mem.containsAtLeastScalar2 in favor of std.mem.containsAtLeastScalar.

- Removed std.mem.readPackedIntNative in favor of std.mem.readPackedInt.

- Removed std.mem.readPackedIntForeign in favor of std.mem.readPackedInt.

- Removed std.mem.writePackedIntNative in favor of std.mem.writePackedInt.

- Removed std.mem.writePackedIntForeign in favor of std.mem.writePackedInt.

StackFallbackAllocator is an abstraction that is useful for the "small

vec" optimization, in which common cases can fit on a pre-allocated stack buffer, but rare cases

need dynamic heap allocation. The previous design had a few problems:

- There was no way to specify the alignment of the buffer.

- It was generic over the size of the buffer.

- Calling .allocator().get() mutated the type, unlike all other allocators,

requiring a runtime safety check.

Now, the buffer is provided as an argument, like most other std APIs that need a buffer.

Migration guide:

std.heap.DebugAllocator is replaced by a thread-safe allocator with the following

guarantees:

- deinit reports all leaks and frees all backing memory.

- All allocation mismatches result in either a panic or segmentation fault.

- Allocations from other SafeAllocator instances cause a panic (if Options.canary differ).

- Double frees and operation (resize, remap, and free) races panic or segmentation fault.

Given the backing allocator does not reuse memory, it does not reuse memory either and most writes after free will segmentation fault or are eventually detected and panic.

std.heap.DebugAllocator and std.heap.Check are

deprecated.

Every allocation is trailed by an AllocFooter which contains metadata for the

allocation and stack traces. It is protected by a checksum to catch corruption from allocation overwrites

and report canary mismatches. An allocation's memory has a minimum alignment of

AllocFooter so that the footer is at a fixed offset determined from the allocation

size. An allocation's memory is stored either:

- Inside linearly-filled buckets for small allocations.

- Inside an allocation directly from the backing allocator.

To track allocations, each thread maintains a table of backing allocations. The table may be modified by other threads in the case of a producer-consumer operation, so the table is a linked list only expanded by creating new segments. Each thread maintains a linked list of free entries, which may contain entries from other threads' tables.

In the case of producer-consumer operations, acquire/release ordering is assumed to be provided externally. This is also assumed by all other thread-safe allocators that reuse memory as otherwise there would be data races on reuse of allocated memory.

Two fuzz tests have also been added for the allocator. They check that there is no memory reuse, that returned memory is writable, and that it is not overwritten. The multi-threaded fuzz test spawns a number of worker threads which are used for all the test runs. I have run these tests extensively under TSAN.

Building the standard library tests with an -Osafe compiler build and

-Ddebug-allocator:

Benchmark 1 (3 runs): ./master-out/bin/zig test --zig-lib-dir lib lib/std/std.zig -femit-bin=test --test-no-exec

measurement mean min ... max outliers delta

wall_time 29.4s 157ms 29.2s ... 29.5s 0 ( 0%) 0%

peak_rss 2.24GB 3.49MB 2.23GB ... 2.24GB 0 ( 0%) 0%

cpu_cycles 143G 999M 142G ... 144G 0 ( 0%) 0%

instructions 268G 5.22M 268G ... 268G 0 ( 0%) 0%

cache_references 13.1G 88.8M 13.0G ... 13.2G 0 ( 0%) 0%

cache_misses 2.38G 30.7M 2.35G ... 2.41G 0 ( 0%) 0%

branch_misses 634M 6.22M 629M ... 641M 0 ( 0%) 0%

Benchmark 2 (3 runs): ./branch-out/bin/zig test --zig-lib-dir lib lib/std/std.zig -femit-bin=test --test-no-exec

measurement mean min ... max outliers delta

wall_time 22.1s 88.6ms 22.0s ... 22.2s 0 ( 0%) - 24.7% 1.0%

peak_rss 1.11GB 799KB 1.11GB ... 1.11GB 0 ( 0%) - 50.3% 0.3%

cpu_cycles 136G 480M 136G ... 137G 0 ( 0%) - 4.4% 1.2%

instructions 273G 2.07M 273G ... 273G 0 ( 0%) + 1.6% 0.0%

cache_references 12.3G 71.3M 12.2G ... 12.4G 0 ( 0%) - 6.0% 1.4%

cache_misses 2.02G 11.5M 2.01G ... 2.03G 0 ( 0%) - 14.9% 2.2%

branch_misses 569M 2.65M 567M ... 572M 0 ( 0%) - 10.2% 1.7%

- getLastOrNull has been deprecated and renamed to last

- getLast has been deprecated in favor of last combined

with .?

- lastPtr has been added which returns ?*T

This is an enhancement that can help track down ArrayList usage bugs faster (#36239).

debug.SafetyLock Gains Support for Shared Locking

The existing lock and unlock methods continue to act

exclusively. They should be used when data may be mutated. New methods, lockShared

and unlockShared, may be used for shared locking in situations where multiple

independent users are reading but not mutating data.

fmt.allocPrint moved to mem.Allocator

The "{q}" specifier which escapes strings so that they can appear in

double-quoted string literals has relaxed escaping rules such that UTF-8 encoded data can pass through

unmangled.

"{qf}" is introduced for double-quote escaping the output of

a format().

std.zon.parse Reworked

std.zon.parse now takes struct args and allocates its result from an arena.

Some methods were renamed:

- fromSliceAlloc fromSlice

- fromSlice fromSliceNoAlloc

- The other "from" methods were renamed following this same scheme.

"updateFrom" variants such as updateFromSlice were added. These update an in

memory value, overwriting the value's fields with fields specified in the ZON source. This can be useful

when using ZON to load configuration files with varying precedence, for example a text editor that has a

global config file and a per-project config file.

bit_set Variants and Deprecate the Managed One

Renames the types for consistency, deprecating the previous names and the managed variant.

- std.bit_set.IntegerBitSet std.bit_set.Integer

- std.bit_set.ArrayBitSet std.bit_set.Array

- std.StaticBitset, std.bit_set.StaticBitSet std.bit_set.Static

- std.DynamicBitSetUnmanaged, std.bit_set.DynamicBitSetUnmanaged std.bit_set.Dynamic

- std.DynamicBitSet, std.bit_set.DynamicBitSet std.bit_set.DynamicManaged (deprecated)

std.lang.Type

When doing type reflection, structs and unions return their information in struct-of-arrays style (#35234).

--- a/lib/compiler/Maker/ScannedConfig.zig

+++ b/lib/compiler/Maker/ScannedConfig.zig

@@ -49,9 +49,10 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {

}

fn printStruct(sc: *const ScannedConfig, s: *Serializer.Struct, comptime S: type, v: S) !void {

- inline for (@typeInfo(S).@"struct".fields) |field| {

- try s.fieldPrefix(field.name);

- try printValue(sc, s.container.serializer, field.type, @field(v, field.name));

+ const info = @typeInfo(S).@"struct";

+ inline for (info.field_names, info.field_types) |field_name, field_type| {

+ try s.fieldPrefix(field_name);

+ try printValue(sc, s.container.serializer, field_type, @field(v, field_name));

}

}

lang.OptimizeMode to lang.Optimize

And remove "release" from the enum tag names.

No functional change, however, despite the addition of backwards-compatibile declarations in this patch,

it is breaking because expressions that use == or !=

operators will not able to use the deprecated names.

- std.lang: OptimizeMode Optimize

- Debug - debug

- ReleaseSafe - safe

- ReleaseFast - fast

- ReleaseSmall - small

lang.Optimize.runtimeSafety

std.lang.Optimize.runtimeSafety is preferred as an alternative to

std.debug.runtime_safety since it will offer callsites knowledge about their own

module rather than standard library module.

The redundant constants cpu, os,

abi, and object_format in

@import("builtin") have been

deprecated and will be removed in 0.18.0. Please replace any usage with the corresponding fields on the

target constant:

- @import("builtin").cpu @import("builtin").target.cpu

- @import("builtin").os @import("builtin").target.os

- @import("builtin").abi @import("builtin").target.abi

- @import("builtin").object_format @import("builtin").target.ofmt

mem.eql and mem.findDiff

The functions std.mem.eql and std.mem.findDiff

short-circuit when their two inputs are slices to the same memory. This short-circuiting is only correct

when the == operator, for the given type, is reflexive. This isn't the case for

floats, as for example std.math.nan(f64) != std.math.nan(f64). The change in this PR

disables that optimisation when working on float slices.

Previously-failing, now-succeeding tests:

Uri and net.HostName

Uri was sometimes using HostName.validate for host (in resolveInPlace) and sometimes not (in

parseAfterScheme). On its own, this was a problem, but the bigger problem is that RFC3986 (Uri) has a much

different idea of what a valid host name is than RFC1123 (HostName), and so just making Uri consistently

use HostName.validate would make Uri less useful overall.

Instead, all HostName-related stuff has been removed from

Uri. Uri.getHost has been moved to

HostName.fromUri (without a graceful deprecation, since the semantics are different enough for users to

need to evaluate usage sites), while Uri.getHostAlloc has been removed entirely.

- b.build_root (Directory) b.root (Path)

- ConfigHeader.Options: include_guard_override include_guard

- LazyPath: getDisplayName format ("{f}")

- LazyPath.basename: removed since the value is not known until make phase

- b.findProgram divided into findProgram and findProgramLazy and API future-proofed.

- ConfigHeader fixed; now reports unused values for all styles

- addArtifactArg, addPrefixedArtifactArg addArtifactArg2

- addOutputFileArg, addPrefixedOutputFileArg addOutputFileArg2

- addFileContentArg, addPrefixedFileContentArg addFileContentArg2

- addOutputDirectoryArg, addPrefixedOutputDirectoryArg addOutputDirectoryArg2

- addDirectoryArg, addPrefixedDirectoryArg, addDecoratedDirectoryArg addDirectoryArg2

- addDepFileOutputArg, addPrefixedDepFileOutputArg addDepFileOutputArg2

- addFileArg, addPrefixedFileArg addFileArg2

zig build now runs projects' build.zig code in a separate executable than the

one that performs Package Management and executes the build graph, making

zig build faster for several reasons

(#35428):

- The maker executable remains unmodified when build.zig script is edited,

and therefore only needs to be built exactly once ("first time setup") after installing Zig.

- The maker executable is built with optimizations enabled, which is starting to become

more valuable now that we have introduced --watch and --fuzz.

- build.zig logic can be skipped sometimes depending on what CLI flags are used with zig build.

Furthermore, configuration is now serialized into a compact binary format that can be consumed by third party tooling and is part of the new Build Server Protocol. The prior way of satisfying this use case by forking the build runner is no longer supported.

To render configuration as .zon to stdout, pass --print-configuration.

New features:

- Directory support. Ability for entries added, removed, or renamed in directories to cause a cache miss.

- Metadata mode. Normally, only changed contents causes a cache miss. In metadata mode, when size, inode, or mtime changes, it always causes a cache miss independent of contents.

All four combinations are possible (is_directory=true/false, metadata_mode=true/false). These features are exposed as new API in the Build System.

Since the Zig toolchain is heavily reliant on the caching system, this release also switches to a

Next page | Previous page | More Lobsters | Headlines

Original: https://ziglang.org/download/0.17.0/release-notes.html