| ... | ... | @@ -3167,24 +3167,81 @@ test "linked list" { |
| 3167 | 3167 | |
| 3168 | 3168 | {#header_open|Default Field Values#} |
| 3169 | 3169 | <p> |
| 3170 | | Each struct field may have an expression indicating the default field value. Such expressions |
| 3171 | | are executed at {#link|comptime#}, and allow the field to be omitted in a struct literal expression: |
| 3170 | Each struct field may have an expression indicating the default field |
| 3171 | value. Such expressions are executed at {#link|comptime#}, and allow the |
| 3172 | field to be omitted in a struct literal expression: |
| 3172 | 3173 | </p> |
| 3173 | | {#code_begin|test|test_struct_default_field_values#} |
| 3174 | {#code_begin|test|struct_default_field_values#} |
| 3174 | 3175 | const Foo = struct { |
| 3175 | 3176 | a: i32 = 1234, |
| 3176 | 3177 | b: i32, |
| 3177 | 3178 | }; |
| 3178 | 3179 | |
| 3179 | 3180 | test "default struct initialization fields" { |
| 3180 | | const x = Foo{ |
| 3181 | const x: Foo = .{ |
| 3181 | 3182 | .b = 5, |
| 3182 | 3183 | }; |
| 3183 | 3184 | if (x.a + x.b != 1239) { |
| 3184 | | @compileError("it's even comptime-known!"); |
| 3185 | comptime unreachable; |
| 3185 | 3186 | } |
| 3186 | 3187 | } |
| 3187 | 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 | 3245 | {#header_close#} |
| 3189 | 3246 | |
| 3190 | 3247 | {#header_open|extern struct#} |