diff --git a/build.zig b/build.zig index 4f60f26756..7e26c84b58 100644 --- a/build.zig +++ b/build.zig @@ -210,9 +210,9 @@ pub fn build(b: *Builder) !void { 2 => { // Untagged development build (e.g. 0.9.0-dev.2025+ecf0050a9). var it = mem.split(u8, git_describe, "-"); - const tagged_ancestor = it.next() orelse unreachable; - const commit_height = it.next() orelse unreachable; - const commit_id = it.next() orelse unreachable; + const tagged_ancestor = it.first(); + const commit_height = it.next().?; + const commit_id = it.next().?; const ancestor_ver = try std.builtin.Version.parse(tagged_ancestor); if (zig_version.order(ancestor_ver) != .gt) { @@ -764,7 +764,7 @@ fn findAndParseConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?CMakeCon inline for (mappings) |mapping| { if (mem.startsWith(u8, line, mapping.prefix)) { var it = mem.split(u8, line, "\""); - _ = it.next().?; // skip the stuff before the quote + _ = it.first(); // skip the stuff before the quote const quoted = it.next().?; // the stuff inside the quote @field(ctx, mapping.field) = toNativePathSep(b, quoted); } diff --git a/lib/std/SemanticVersion.zig b/lib/std/SemanticVersion.zig index fb09f85ff2..cd0d48fba7 100644 --- a/lib/std/SemanticVersion.zig +++ b/lib/std/SemanticVersion.zig @@ -88,7 +88,7 @@ pub fn parse(text: []const u8) !Version { const required = text[0..(extra_index orelse text.len)]; var it = std.mem.split(u8, required, "."); var ver = Version{ - .major = try parseNum(it.next() orelse return error.InvalidVersion), + .major = try parseNum(it.first()), .minor = try parseNum(it.next() orelse return error.InvalidVersion), .patch = try parseNum(it.next() orelse return error.InvalidVersion), }; diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 68de3e9ddd..c38eb543ed 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -503,7 +503,7 @@ pub const Version = struct { var it = std.mem.split(u8, text[0..end], "."); // substring is not empty, first call will succeed - const major = it.next().?; + const major = it.first(); if (major.len == 0) return error.InvalidVersion; const minor = it.next() orelse "0"; // ignore 'patch' if 'minor' is invalid diff --git a/lib/std/crypto/phc_encoding.zig b/lib/std/crypto/phc_encoding.zig index 69324dc5b3..2c672f6bd5 100644 --- a/lib/std/crypto/phc_encoding.zig +++ b/lib/std/crypto/phc_encoding.zig @@ -253,7 +253,7 @@ fn serializeTo(params: anytype, out: anytype) !void { // Split a `key=value` string into `key` and `value` fn kvSplit(str: []const u8) !struct { key: []const u8, value: []const u8 } { var it = mem.split(u8, str, kv_delimiter); - const key = it.next() orelse return Error.InvalidEncoding; + const key = it.first(); const value = it.next() orelse return Error.InvalidEncoding; const ret = .{ .key = key, .value = value }; return ret; diff --git a/lib/std/crypto/scrypt.zig b/lib/std/crypto/scrypt.zig index 35049ebc51..9b2bf01022 100644 --- a/lib/std/crypto/scrypt.zig +++ b/lib/std/crypto/scrypt.zig @@ -289,7 +289,7 @@ const crypt_format = struct { var it = mem.split(u8, str[14..], "$"); - const salt = it.next() orelse return EncodingError.InvalidEncoding; + const salt = it.first(); if (@hasField(T, "salt")) out.salt = salt; const hash_str = it.next() orelse return EncodingError.InvalidEncoding; diff --git a/lib/std/mem.zig b/lib/std/mem.zig index b0338bdf20..333005cc44 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1597,12 +1597,15 @@ test "byteSwapAllFields" { /// Returns an iterator that iterates over the slices of `buffer` that are not /// any of the bytes in `delimiter_bytes`. -/// tokenize(u8, " abc def ghi ", " ") -/// Will return slices for "abc", "def", "ghi", null, in that order. +/// +/// `tokenize(u8, " abc def ghi ", " ")` will return slices +/// for "abc", "def", "ghi", null, in that order. +/// /// If `buffer` is empty, the iterator will return null. /// If `delimiter_bytes` does not exist in buffer, /// the iterator will return `buffer`, null, in that order. -/// See also the related function `split`. +/// +/// See also: `split` and `splitBackwards`. pub fn tokenize(comptime T: type, buffer: []const T, delimiter_bytes: []const T) TokenIterator(T) { return .{ .index = 0, @@ -1696,12 +1699,15 @@ test "tokenize (reset)" { /// Returns an iterator that iterates over the slices of `buffer` that /// are separated by bytes in `delimiter`. -/// split(u8, "abc|def||ghi", "|") -/// will return slices for "abc", "def", "", "ghi", null, in that order. +/// +/// `split(u8, "abc|def||ghi", "|")` will return slices +/// for "abc", "def", "", "ghi", null, in that order. +/// /// If `delimiter` does not exist in buffer, /// the iterator will return `buffer`, null, in that order. /// The delimiter length must not be zero. -/// See also the related function `tokenize`. +/// +/// See also: `tokenize` and `splitBackwards`. pub fn split(comptime T: type, buffer: []const T, delimiter: []const T) SplitIterator(T) { assert(delimiter.len != 0); return .{ @@ -1714,7 +1720,7 @@ pub fn split(comptime T: type, buffer: []const T, delimiter: []const T) SplitIte test "split" { var it = split(u8, "abc|def||ghi", "|"); try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi"); - try testing.expectEqualSlices(u8, it.next().?, "abc"); + try testing.expectEqualSlices(u8, it.first(), "abc"); try testing.expectEqualSlices(u8, it.rest(), "def||ghi"); try testing.expectEqualSlices(u8, it.next().?, "def"); @@ -1729,16 +1735,16 @@ test "split" { try testing.expect(it.next() == null); it = split(u8, "", "|"); - try testing.expectEqualSlices(u8, it.next().?, ""); + try testing.expectEqualSlices(u8, it.first(), ""); try testing.expect(it.next() == null); it = split(u8, "|", "|"); - try testing.expectEqualSlices(u8, it.next().?, ""); + try testing.expectEqualSlices(u8, it.first(), ""); try testing.expectEqualSlices(u8, it.next().?, ""); try testing.expect(it.next() == null); it = split(u8, "hello", " "); - try testing.expectEqualSlices(u8, it.next().?, "hello"); + try testing.expectEqualSlices(u8, it.first(), "hello"); try testing.expect(it.next() == null); var it16 = split( @@ -1746,13 +1752,13 @@ test "split" { std.unicode.utf8ToUtf16LeStringLiteral("hello"), std.unicode.utf8ToUtf16LeStringLiteral(" "), ); - try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello")); + try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("hello")); try testing.expect(it16.next() == null); } test "split (multibyte)" { var it = split(u8, "a, b ,, c, d, e", ", "); - try testing.expectEqualSlices(u8, it.next().?, "a"); + try testing.expectEqualSlices(u8, it.first(), "a"); try testing.expectEqualSlices(u8, it.rest(), "b ,, c, d, e"); try testing.expectEqualSlices(u8, it.next().?, "b ,"); try testing.expectEqualSlices(u8, it.next().?, "c"); @@ -1765,7 +1771,7 @@ test "split (multibyte)" { std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"), std.unicode.utf8ToUtf16LeStringLiteral(", "), ); - try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a")); + try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("a")); try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,")); try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); @@ -1775,11 +1781,15 @@ test "split (multibyte)" { /// Returns an iterator that iterates backwards over the slices of `buffer` /// that are separated by bytes in `delimiter`. -/// splitBackwards(u8, "abc|def||ghi", "|") -/// will return slices for "ghi", "", "def", "abc", null, in that order. +/// +/// `splitBackwards(u8, "abc|def||ghi", "|")` will return slices +/// for "ghi", "", "def", "abc", null, in that order. +/// /// If `delimiter` does not exist in buffer, /// the iterator will return `buffer`, null, in that order. /// The delimiter length must not be zero. +/// +/// See also: `tokenize` and `split`. pub fn splitBackwards(comptime T: type, buffer: []const T, delimiter: []const T) SplitBackwardsIterator(T) { assert(delimiter.len != 0); return SplitBackwardsIterator(T){ @@ -1792,7 +1802,7 @@ pub fn splitBackwards(comptime T: type, buffer: []const T, delimiter: []const T) test "splitBackwards" { var it = splitBackwards(u8, "abc|def||ghi", "|"); try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi"); - try testing.expectEqualSlices(u8, it.next().?, "ghi"); + try testing.expectEqualSlices(u8, it.first(), "ghi"); try testing.expectEqualSlices(u8, it.rest(), "abc|def|"); try testing.expectEqualSlices(u8, it.next().?, ""); @@ -1807,16 +1817,16 @@ test "splitBackwards" { try testing.expect(it.next() == null); it = splitBackwards(u8, "", "|"); - try testing.expectEqualSlices(u8, it.next().?, ""); + try testing.expectEqualSlices(u8, it.first(), ""); try testing.expect(it.next() == null); it = splitBackwards(u8, "|", "|"); - try testing.expectEqualSlices(u8, it.next().?, ""); + try testing.expectEqualSlices(u8, it.first(), ""); try testing.expectEqualSlices(u8, it.next().?, ""); try testing.expect(it.next() == null); it = splitBackwards(u8, "hello", " "); - try testing.expectEqualSlices(u8, it.next().?, "hello"); + try testing.expectEqualSlices(u8, it.first(), "hello"); try testing.expect(it.next() == null); var it16 = splitBackwards( @@ -1824,14 +1834,14 @@ test "splitBackwards" { std.unicode.utf8ToUtf16LeStringLiteral("hello"), std.unicode.utf8ToUtf16LeStringLiteral(" "), ); - try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello")); + try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("hello")); try testing.expect(it16.next() == null); } test "splitBackwards (multibyte)" { var it = splitBackwards(u8, "a, b ,, c, d, e", ", "); try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d, e"); - try testing.expectEqualSlices(u8, it.next().?, "e"); + try testing.expectEqualSlices(u8, it.first(), "e"); try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d"); try testing.expectEqualSlices(u8, it.next().?, "d"); @@ -1853,7 +1863,7 @@ test "splitBackwards (multibyte)" { std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"), std.unicode.utf8ToUtf16LeStringLiteral(", "), ); - try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e")); + try testing.expectEqualSlices(u16, it16.first(), std.unicode.utf8ToUtf16LeStringLiteral("e")); try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,")); @@ -1944,6 +1954,13 @@ pub fn SplitIterator(comptime T: type) type { const Self = @This(); + /// Returns a slice of the first field. This never fails. + /// Call this only to get the first field and then use `next` to get all subsequent fields. + pub fn first(self: *Self) []const T { + assert(self.index.? == 0); + return self.next().?; + } + /// Returns a slice of the next field, or null if splitting is complete. pub fn next(self: *Self) ?[]const T { const start = self.index orelse return null; @@ -1974,6 +1991,13 @@ pub fn SplitBackwardsIterator(comptime T: type) type { const Self = @This(); + /// Returns a slice of the first field. This never fails. + /// Call this only to get the first field and then use `next` to get all subsequent fields. + pub fn first(self: *Self) []const T { + assert(self.index.? == self.buffer.len); + return self.next().?; + } + /// Returns a slice of the next field, or null if splitting is complete. pub fn next(self: *Self) ?[]const T { const end = self.index orelse return null; diff --git a/lib/std/net.zig b/lib/std/net.zig index c381df9bce..21fa36c4eb 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1154,7 +1154,7 @@ fn linuxLookupNameFromHosts( else => |e| return e, }) |line| { var split_it = mem.split(u8, line, "#"); - const no_comment_line = split_it.next().?; + const no_comment_line = split_it.first(); var line_it = mem.tokenize(u8, no_comment_line, " \t"); const ip_text = line_it.next() orelse continue; @@ -1356,7 +1356,7 @@ fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void { }) |line| { const no_comment_line = no_comment_line: { var split = mem.split(u8, line, "#"); - break :no_comment_line split.next().?; + break :no_comment_line split.first(); }; var line_it = mem.tokenize(u8, no_comment_line, " \t"); @@ -1364,7 +1364,7 @@ fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void { if (mem.eql(u8, token, "options")) { while (line_it.next()) |sub_tok| { var colon_it = mem.split(u8, sub_tok, ":"); - const name = colon_it.next().?; + const name = colon_it.first(); const value_txt = colon_it.next() orelse continue; const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) { // TODO https://github.com/ziglang/zig/issues/11812 diff --git a/lib/std/process.zig b/lib/std/process.zig index dffd6b1701..d8604edab8 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -306,7 +306,7 @@ pub fn getEnvMap(allocator: Allocator) !EnvMap { for (environ) |env| { const pair = mem.sliceTo(env, 0); var parts = mem.split(u8, pair, "="); - const key = parts.next().?; + const key = parts.first(); const value = parts.next().?; try result.put(key, value); } diff --git a/lib/std/zig/CrossTarget.zig b/lib/std/zig/CrossTarget.zig index 05567d7d09..196afbc835 100644 --- a/lib/std/zig/CrossTarget.zig +++ b/lib/std/zig/CrossTarget.zig @@ -231,7 +231,7 @@ pub fn parse(args: ParseOptions) !CrossTarget { }; var it = mem.split(u8, args.arch_os_abi, "-"); - const arch_name = it.next().?; + const arch_name = it.first(); const arch_is_native = mem.eql(u8, arch_name, "native"); if (!arch_is_native) { result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse @@ -249,7 +249,7 @@ pub fn parse(args: ParseOptions) !CrossTarget { const opt_abi_text = it.next(); if (opt_abi_text) |abi_text| { var abi_it = mem.split(u8, abi_text, "."); - const abi = std.meta.stringToEnum(Target.Abi, abi_it.next().?) orelse + const abi = std.meta.stringToEnum(Target.Abi, abi_it.first()) orelse return error.UnknownApplicationBinaryInterface; result.abi = abi; diags.abi = abi; @@ -330,7 +330,7 @@ pub fn parse(args: ParseOptions) !CrossTarget { /// target CPU architecture in order to fully populate `ParseOptions`. pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch { var it = mem.split(u8, args.arch_os_abi, "-"); - const arch_name = it.next().?; + const arch_name = it.first(); const arch_is_native = mem.eql(u8, arch_name, "native"); if (arch_is_native) { return builtin.cpu.arch; @@ -632,7 +632,7 @@ pub fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void { fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void { var it = mem.split(u8, text, "."); - const os_name = it.next().?; + const os_name = it.first(); diags.os_name = os_name; const os_is_native = mem.eql(u8, os_name, "native"); if (!os_is_native) { @@ -711,7 +711,7 @@ fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const .windows => { var range_it = mem.split(u8, version_text, "..."); - const min_text = range_it.next().?; + const min_text = range_it.first(); const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse return error.InvalidOperatingSystemVersion; result.os_version_min = .{ .windows = min_ver }; diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 72d710b29a..03f951d0f1 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -1820,7 +1820,7 @@ fn renderArrayInit( } else { var by_line = std.mem.split(u8, expr_text, "\n"); var last_line_was_empty = false; - try ais.writer().writeAll(by_line.next().?); + try ais.writer().writeAll(by_line.first()); while (by_line.next()) |line| { if (std.mem.startsWith(u8, line, "//") and last_line_was_empty) { try ais.insertNewline(); diff --git a/src/Compilation.zig b/src/Compilation.zig index 5204910193..347f1ea0f5 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4392,7 +4392,7 @@ pub fn hasSharedLibraryExt(filename: []const u8) bool { } // Look for .so.X, .so.X.Y, .so.X.Y.Z var it = mem.split(u8, filename, "."); - _ = it.next().?; + _ = it.first(); var so_txt = it.next() orelse return false; while (!mem.eql(u8, so_txt, "so")) { so_txt = it.next() orelse return false; diff --git a/src/libc_installation.zig b/src/libc_installation.zig index 0b40580d7b..f9f6b37fba 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -64,10 +64,7 @@ pub const LibCInstallation = struct { while (it.next()) |line| { if (line.len == 0 or line[0] == '#') continue; var line_it = std.mem.split(u8, line, "="); - const name = line_it.next() orelse { - log.err("missing equal sign after field name\n", .{}); - return error.ParseError; - }; + const name = line_it.first(); const value = line_it.rest(); inline for (fields) |field, i| { if (std.mem.eql(u8, name, field.name)) { diff --git a/src/test.zig b/src/test.zig index 20a7d1673a..554248c2de 100644 --- a/src/test.zig +++ b/src/test.zig @@ -303,7 +303,7 @@ const TestManifest = struct { // Parse key=value(s) var kv_it = std.mem.split(u8, trimmed, "="); - const key = kv_it.next() orelse return error.MissingKeyForConfig; + const key = kv_it.first(); try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig); } @@ -697,7 +697,7 @@ pub const TestContext = struct { } // example: "file.zig:1:2: error: bad thing happened" var it = std.mem.split(u8, err_msg_line, ":"); - const src_path = it.next() orelse @panic("missing colon"); + const src_path = it.first(); const line_text = it.next() orelse @panic("missing line"); const col_text = it.next() orelse @panic("missing column"); const kind_text = it.next() orelse @panic("missing 'error'/'note'"); @@ -1698,7 +1698,7 @@ pub const TestContext = struct { var fib = std.io.fixedBufferStream(&buf); try msg.renderToWriter(.no_color, fib.writer(), "error", .Red, 0); var it = std.mem.split(u8, fib.getWritten(), "error: "); - _ = it.next(); + _ = it.first(); const rendered = it.rest(); break :blk rendered[0 .. rendered.len - 1]; // trim final newline }; diff --git a/tools/update_spirv_features.zig b/tools/update_spirv_features.zig index b32356e815..8d20039fa1 100644 --- a/tools/update_spirv_features.zig +++ b/tools/update_spirv_features.zig @@ -21,7 +21,7 @@ const Version = struct { fn parse(str: []const u8) !Version { var it = std.mem.split(u8, str, "."); - const major = it.next() orelse return error.InvalidVersion; + const major = it.first(); const minor = it.next() orelse return error.InvalidVersion; if (it.next() != null) return error.InvalidVersion;