diff --git a/doc/langref.html.in b/doc/langref.html.in index 98b0ccca5c..9d48238843 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3167,24 +3167,81 @@ test "linked list" { {#header_open|Default Field Values#}

- Each struct field may have an expression indicating the default field value. Such expressions - are executed at {#link|comptime#}, and allow the field to be omitted in a struct literal expression: + Each struct field may have an expression indicating the default field + value. Such expressions are executed at {#link|comptime#}, and allow the + field to be omitted in a struct literal expression:

- {#code_begin|test|test_struct_default_field_values#} + {#code_begin|test|struct_default_field_values#} const Foo = struct { a: i32 = 1234, b: i32, }; test "default struct initialization fields" { - const x = Foo{ + const x: Foo = .{ .b = 5, }; if (x.a + x.b != 1239) { - @compileError("it's even comptime-known!"); + comptime unreachable; } } {#code_end#} +

+ Default field values are only appropriate when the data invariants of a struct + cannot be violated by omitting that field from an initialization. +

+

+ For example, here is an inappropriate use of default struct field initialization: +

+ {#code_begin|exe_err|bad_default_value#} +const Threshold = struct { + minimum: f32 = 0.25, + maximum: f32 = 0.75, + + const Category = enum { low, medium, high }; + + fn categorize(t: Threshold, value: f32) Category { + assert(t.maximum >= t.minimum); + if (value < t.minimum) return .low; + if (value > t.maximum) return .high; + return .medium; + } +}; + +pub fn main() !void { + var threshold: Threshold = .{ + .maximum = 0.20, + }; + const category = threshold.categorize(0.90); + try std.io.getStdOut().writeAll(@tagName(category)); +} + +const std = @import("std"); +const assert = std.debug.assert; + {#code_end#} +

+ Above you can see the danger of ignoring this principle. The default + field values caused the data invariant to be violated, causing illegal + behavior. +

+

+ To fix this, remove the default values from all the struct fields, and provide + a named default value: +

+ {#code_begin|syntax|struct_default_value#} +const Threshold = struct { + minimum: f32, + maximum: f32, + + const default: Threshold = .{ + .minimum = 0.25, + .maximum = 0.75, + }; +}; + {#code_end#} +

If a struct value requires a runtime-known value in order to be initialized + without violating data invariants, then use an initialization method that accepts + those runtime values, and populates the remaining fields.

{#header_close#} {#header_open|extern struct#}