| ... | @@ -3167,24 +3167,81 @@ test "linked list" { | ... | @@ -3167,24 +3167,81 @@ test "linked list" { |
| 3167 | | 3167 | |
| 3168 | {#header_open|Default Field Values#} | 3168 | {#header_open|Default Field Values#} |
| 3169 | <p> | 3169 | <p> |
| 3170 | Each struct field may have an expression indicating the default field value. Such expressions | 3170 | Each struct field may have an expression indicating the default field |
| 3171 | are executed at {#link|comptime#}, and allow the field to be omitted in a struct literal expression: | 3171 | value. Such expressions are executed at {#link|comptime#}, and allow the |
| | 3172 | field to be omitted in a struct literal expression: |
| 3172 | </p> | 3173 | </p> |
| 3173 | {#code_begin|test|test_struct_default_field_values#} | 3174 | {#code_begin|test|struct_default_field_values#} |
| 3174 | const Foo = struct { | 3175 | const Foo = struct { |
| 3175 | a: i32 = 1234, | 3176 | a: i32 = 1234, |
| 3176 | b: i32, | 3177 | b: i32, |
| 3177 | }; | 3178 | }; |
| 3178 | | 3179 | |
| 3179 | test "default struct initialization fields" { | 3180 | test "default struct initialization fields" { |
| 3180 | const x = Foo{ | 3181 | const x: Foo = .{ |
| 3181 | .b = 5, | 3182 | .b = 5, |
| 3182 | }; | 3183 | }; |
| 3183 | if (x.a + x.b != 1239) { | 3184 | if (x.a + x.b != 1239) { |
| 3184 | @compileError("it's even comptime-known!"); | 3185 | comptime unreachable; |
| 3185 | } | 3186 | } |
| 3186 | } | 3187 | } |
| 3187 | {#code_end#} | 3188 | {#code_end#} |
| | 3189 | <p> |
| | 3190 | Default field values are only appropriate when the data invariants of a struct |
| | 3191 | cannot be violated by omitting that field from an initialization. |
| | 3192 | </p> |
| | 3193 | <p> |
| | 3194 | For example, here is an inappropriate use of default struct field initialization: |
| | 3195 | </p> |
| | 3196 | {#code_begin|exe_err|bad_default_value#} |
| | 3197 | const Threshold = struct { |
| | 3198 | minimum: f32 = 0.25, |
| | 3199 | maximum: f32 = 0.75, |
| | 3200 | |
| | 3201 | const Category = enum { low, medium, high }; |
| | 3202 | |
| | 3203 | fn categorize(t: Threshold, value: f32) Category { |
| | 3204 | assert(t.maximum >= t.minimum); |
| | 3205 | if (value < t.minimum) return .low; |
| | 3206 | if (value > t.maximum) return .high; |
| | 3207 | return .medium; |
| | 3208 | } |
| | 3209 | }; |
| | 3210 | |
| | 3211 | pub fn main() !void { |
| | 3212 | var threshold: Threshold = .{ |
| | 3213 | .maximum = 0.20, |
| | 3214 | }; |
| | 3215 | const category = threshold.categorize(0.90); |
| | 3216 | try std.io.getStdOut().writeAll(@tagName(category)); |
| | 3217 | } |
| | 3218 | |
| | 3219 | const std = @import("std"); |
| | 3220 | const assert = std.debug.assert; |
| | 3221 | {#code_end#} |
| | 3222 | <p> |
| | 3223 | Above you can see the danger of ignoring this principle. The default |
| | 3224 | field values caused the data invariant to be violated, causing illegal |
| | 3225 | behavior. |
| | 3226 | </p> |
| | 3227 | <p> |
| | 3228 | To fix this, remove the default values from all the struct fields, and provide |
| | 3229 | a named default value: |
| | 3230 | </p> |
| | 3231 | {#code_begin|syntax|struct_default_value#} |
| | 3232 | const Threshold = struct { |
| | 3233 | minimum: f32, |
| | 3234 | maximum: f32, |
| | 3235 | |
| | 3236 | const default: Threshold = .{ |
| | 3237 | .minimum = 0.25, |
| | 3238 | .maximum = 0.75, |
| | 3239 | }; |
| | 3240 | }; |
| | 3241 | {#code_end#} |
| | 3242 | <p>If a struct value requires a runtime-known value in order to be initialized |
| | 3243 | without violating data invariants, then use an initialization method that accepts |
| | 3244 | those runtime values, and populates the remaining fields.</p> |
| 3188 | {#header_close#} | 3245 | {#header_close#} |
| 3189 | | 3246 | |
| 3190 | {#header_open|extern struct#} | 3247 | {#header_open|extern struct#} |