| ... | @@ -4771,6 +4771,8 @@ test "parse u64" { | ... | @@ -4771,6 +4771,8 @@ test "parse u64" { |
| 4771 | {#header_open|catch#} | 4771 | {#header_open|catch#} |
| 4772 | <p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p> | 4772 | <p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p> |
| 4773 | {#code_begin|syntax#} | 4773 | {#code_begin|syntax#} |
| | 4774 | const parseU64 = @import("error_union_parsing_u64.zig").parseU64; |
| | 4775 | |
| 4774 | fn doAThing(str: []u8) void { | 4776 | fn doAThing(str: []u8) void { |
| 4775 | const number = parseU64(str, 10) catch 13; | 4777 | const number = parseU64(str, 10) catch 13; |
| 4776 | _ = number; // ... | 4778 | _ = number; // ... |
| ... | @@ -4786,6 +4788,8 @@ fn doAThing(str: []u8) void { | ... | @@ -4786,6 +4788,8 @@ fn doAThing(str: []u8) void { |
| 4786 | <p>Let's say you wanted to return the error if you got one, otherwise continue with the | 4788 | <p>Let's say you wanted to return the error if you got one, otherwise continue with the |
| 4787 | function logic:</p> | 4789 | function logic:</p> |
| 4788 | {#code_begin|syntax#} | 4790 | {#code_begin|syntax#} |
| | 4791 | const parseU64 = @import("error_union_parsing_u64.zig").parseU64; |
| | 4792 | |
| 4789 | fn doAThing(str: []u8) !void { | 4793 | fn doAThing(str: []u8) !void { |
| 4790 | const number = parseU64(str, 10) catch |err| return err; | 4794 | const number = parseU64(str, 10) catch |err| return err; |
| 4791 | _ = number; // ... | 4795 | _ = number; // ... |
| ... | @@ -4795,6 +4799,8 @@ fn doAThing(str: []u8) !void { | ... | @@ -4795,6 +4799,8 @@ fn doAThing(str: []u8) !void { |
| 4795 | There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression: | 4799 | There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression: |
| 4796 | </p> | 4800 | </p> |
| 4797 | {#code_begin|syntax#} | 4801 | {#code_begin|syntax#} |
| | 4802 | const parseU64 = @import("error_union_parsing_u64.zig").parseU64; |
| | 4803 | |
| 4798 | fn doAThing(str: []u8) !void { | 4804 | fn doAThing(str: []u8) !void { |
| 4799 | const number = try parseU64(str, 10); | 4805 | const number = try parseU64(str, 10); |
| 4800 | _ = number; // ... | 4806 | _ = number; // ... |
| ... | @@ -4810,7 +4816,7 @@ fn doAThing(str: []u8) !void { | ... | @@ -4810,7 +4816,7 @@ fn doAThing(str: []u8) !void { |
| 4810 | Maybe you know with complete certainty that an expression will never be an error. | 4816 | Maybe you know with complete certainty that an expression will never be an error. |
| 4811 | In this case you can do this: | 4817 | In this case you can do this: |
| 4812 | </p> | 4818 | </p> |
| 4813 | {#code_begin|syntax#}const number = parseU64("1234", 10) catch unreachable;{#code_end#} | 4819 | {#syntax#}const number = parseU64("1234", 10) catch unreachable;{#endsyntax#} |
| 4814 | <p> | 4820 | <p> |
| 4815 | Here we know for sure that "1234" will parse successfully. So we put the | 4821 | Here we know for sure that "1234" will parse successfully. So we put the |
| 4816 | {#syntax#}unreachable{#endsyntax#} value on the right hand side. {#syntax#}unreachable{#endsyntax#} generates | 4822 | {#syntax#}unreachable{#endsyntax#} value on the right hand side. {#syntax#}unreachable{#endsyntax#} generates |
| ... | @@ -4822,7 +4828,7 @@ fn doAThing(str: []u8) !void { | ... | @@ -4822,7 +4828,7 @@ fn doAThing(str: []u8) !void { |
| 4822 | Finally, you may want to take a different action for every situation. For that, we combine | 4828 | Finally, you may want to take a different action for every situation. For that, we combine |
| 4823 | the {#link|if#} and {#link|switch#} expression: | 4829 | the {#link|if#} and {#link|switch#} expression: |
| 4824 | </p> | 4830 | </p> |
| 4825 | {#code_begin|syntax#} | 4831 | {#syntax_block|zig|handle_all_error_scenarios.zig#} |
| 4826 | fn doAThing(str: []u8) void { | 4832 | fn doAThing(str: []u8) void { |
| 4827 | if (parseU64(str, 10)) |number| { | 4833 | if (parseU64(str, 10)) |number| { |
| 4828 | doSomethingWithNumber(number); | 4834 | doSomethingWithNumber(number); |
| ... | @@ -4834,7 +4840,7 @@ fn doAThing(str: []u8) void { | ... | @@ -4834,7 +4840,7 @@ fn doAThing(str: []u8) void { |
| 4834 | error.InvalidChar => unreachable, | 4840 | error.InvalidChar => unreachable, |
| 4835 | } | 4841 | } |
| 4836 | } | 4842 | } |
| 4837 | {#code_end#} | 4843 | {#end_syntax_block#} |
| 4838 | {#header_open|errdefer#} | 4844 | {#header_open|errdefer#} |
| 4839 | <p> | 4845 | <p> |
| 4840 | The other component to error handling is defer statements. | 4846 | The other component to error handling is defer statements. |
| ... | @@ -4845,7 +4851,7 @@ fn doAThing(str: []u8) void { | ... | @@ -4845,7 +4851,7 @@ fn doAThing(str: []u8) void { |
| 4845 | <p> | 4851 | <p> |
| 4846 | Example: | 4852 | Example: |
| 4847 | </p> | 4853 | </p> |
| 4848 | {#code_begin|syntax#} | 4854 | {#syntax_block|zig|errdefer_example.zig#} |
| 4849 | fn createFoo(param: i32) !Foo { | 4855 | fn createFoo(param: i32) !Foo { |
| 4850 | const foo = try tryToAllocateFoo(); | 4856 | const foo = try tryToAllocateFoo(); |
| 4851 | // now we have allocated foo. we need to free it if the function fails. | 4857 | // now we have allocated foo. we need to free it if the function fails. |
| ... | @@ -4863,7 +4869,7 @@ fn createFoo(param: i32) !Foo { | ... | @@ -4863,7 +4869,7 @@ fn createFoo(param: i32) !Foo { |
| 4863 | // but the defer will run! | 4869 | // but the defer will run! |
| 4864 | return foo; | 4870 | return foo; |
| 4865 | } | 4871 | } |
| 4866 | {#code_end#} | 4872 | {#end_syntax_block#} |
| 4867 | <p> | 4873 | <p> |
| 4868 | The neat thing about this is that you get robust error handling without | 4874 | The neat thing about this is that you get robust error handling without |
| 4869 | the verbosity and cognitive overhead of trying to make sure every exit path | 4875 | the verbosity and cognitive overhead of trying to make sure every exit path |
| ... | @@ -5132,12 +5138,12 @@ fn bang2() void { | ... | @@ -5132,12 +5138,12 @@ fn bang2() void { |
| 5132 | For the case when no errors are returned, the cost is a single memory write operation, only in the first non-failable function in the call graph that calls a failable function, i.e. when a function returning {#syntax#}void{#endsyntax#} calls a function returning {#syntax#}error{#endsyntax#}. | 5138 | For the case when no errors are returned, the cost is a single memory write operation, only in the first non-failable function in the call graph that calls a failable function, i.e. when a function returning {#syntax#}void{#endsyntax#} calls a function returning {#syntax#}error{#endsyntax#}. |
| 5133 | This is to initialize this struct in the stack memory: | 5139 | This is to initialize this struct in the stack memory: |
| 5134 | </p> | 5140 | </p> |
| 5135 | {#code_begin|syntax#} | 5141 | {#syntax_block|zig|stack_trace_struct.zig#} |
| 5136 | pub const StackTrace = struct { | 5142 | pub const StackTrace = struct { |
| 5137 | index: usize, | 5143 | index: usize, |
| 5138 | instruction_addresses: [N]usize, | 5144 | instruction_addresses: [N]usize, |
| 5139 | }; | 5145 | }; |
| 5140 | {#code_end#} | 5146 | {#end_syntax_block#} |
| 5141 | <p> | 5147 | <p> |
| 5142 | Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2. | 5148 | Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2. |
| 5143 | </p> | 5149 | </p> |
| ... | @@ -5150,13 +5156,13 @@ pub const StackTrace = struct { | ... | @@ -5150,13 +5156,13 @@ pub const StackTrace = struct { |
| 5150 | <p> | 5156 | <p> |
| 5151 | When generating the code for a function that returns an error, just before the {#syntax#}return{#endsyntax#} statement (only for the {#syntax#}return{#endsyntax#} statements that return errors), Zig generates a call to this function: | 5157 | When generating the code for a function that returns an error, just before the {#syntax#}return{#endsyntax#} statement (only for the {#syntax#}return{#endsyntax#} statements that return errors), Zig generates a call to this function: |
| 5152 | </p> | 5158 | </p> |
| 5153 | {#code_begin|syntax#} | 5159 | {#syntax_block|zig|zig_return_error_fn.zig#} |
| 5154 | // marked as "no-inline" in LLVM IR | 5160 | // marked as "no-inline" in LLVM IR |
| 5155 | fn __zig_return_error(stack_trace: *StackTrace) void { | 5161 | fn __zig_return_error(stack_trace: *StackTrace) void { |
| 5156 | stack_trace.instruction_addresses[stack_trace.index] = @returnAddress(); | 5162 | stack_trace.instruction_addresses[stack_trace.index] = @returnAddress(); |
| 5157 | stack_trace.index = (stack_trace.index + 1) % N; | 5163 | stack_trace.index = (stack_trace.index + 1) % N; |
| 5158 | } | 5164 | } |
| 5159 | {#code_end#} | 5165 | {#end_syntax_block#} |
| 5160 | <p> | 5166 | <p> |
| 5161 | The cost is 2 math operations plus some memory reads and writes. The memory accessed is constrained and should remain cached for the duration of the error return bubbling. | 5167 | The cost is 2 math operations plus some memory reads and writes. The memory accessed is constrained and should remain cached for the duration of the error return bubbling. |
| 5162 | </p> | 5168 | </p> |
| ... | @@ -5206,16 +5212,16 @@ const optional_int: ?i32 = 5678; | ... | @@ -5206,16 +5212,16 @@ const optional_int: ?i32 = 5678; |
| 5206 | Task: call malloc, if the result is null, return null. | 5212 | Task: call malloc, if the result is null, return null. |
| 5207 | </p> | 5213 | </p> |
| 5208 | <p>C code</p> | 5214 | <p>C code</p> |
| 5209 | <pre><code class="cpp">// malloc prototype included for reference | 5215 | {#syntax_block|c|call_malloc_in_c.c#}// malloc prototype included for reference |
| 5210 | void *malloc(size_t size); | 5216 | void *malloc(size_t size); |
| 5211 | | 5217 | |
| 5212 | struct Foo *do_a_thing(void) { | 5218 | struct Foo *do_a_thing(void) { |
| 5213 | char *ptr = malloc(1234); | 5219 | char *ptr = malloc(1234); |
| 5214 | if (!ptr) return NULL; | 5220 | if (!ptr) return NULL; |
| 5215 | // ... | 5221 | // ... |
| 5216 | }</code></pre> | 5222 | }{#end_syntax_block#} |
| 5217 | <p>Zig code</p> | 5223 | <p>Zig code</p> |
| 5218 | {#code_begin|syntax#} | 5224 | {#syntax_block|zig|call_malloc_from_zig.zig#} |
| 5219 | // malloc prototype included for reference | 5225 | // malloc prototype included for reference |
| 5220 | extern fn malloc(size: size_t) ?*u8; | 5226 | extern fn malloc(size: size_t) ?*u8; |
| 5221 | | 5227 | |
| ... | @@ -5223,7 +5229,7 @@ fn doAThing() ?*Foo { | ... | @@ -5223,7 +5229,7 @@ fn doAThing() ?*Foo { |
| 5223 | const ptr = malloc(1234) orelse return null; | 5229 | const ptr = malloc(1234) orelse return null; |
| 5224 | _ = ptr; // ... | 5230 | _ = ptr; // ... |
| 5225 | } | 5231 | } |
| 5226 | {#code_end#} | 5232 | {#end_syntax_block#} |
| 5227 | <p> | 5233 | <p> |
| 5228 | Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr" | 5234 | Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr" |
| 5229 | is {#syntax#}*u8{#endsyntax#} <em>not</em> {#syntax#}?*u8{#endsyntax#}. The {#syntax#}orelse{#endsyntax#} keyword | 5235 | is {#syntax#}*u8{#endsyntax#} <em>not</em> {#syntax#}?*u8{#endsyntax#}. The {#syntax#}orelse{#endsyntax#} keyword |
| ... | @@ -5233,7 +5239,7 @@ fn doAThing() ?*Foo { | ... | @@ -5233,7 +5239,7 @@ fn doAThing() ?*Foo { |
| 5233 | <p> | 5239 | <p> |
| 5234 | The other form of checking against NULL you might see looks like this: | 5240 | The other form of checking against NULL you might see looks like this: |
| 5235 | </p> | 5241 | </p> |
| 5236 | <pre><code class="cpp">void do_a_thing(struct Foo *foo) { | 5242 | {#syntax_block|c|checking_null_in_c.c#}void do_a_thing(struct Foo *foo) { |
| 5237 | // do some stuff | 5243 | // do some stuff |
| 5238 | | 5244 | |
| 5239 | if (foo) { | 5245 | if (foo) { |
| ... | @@ -5241,11 +5247,14 @@ fn doAThing() ?*Foo { | ... | @@ -5241,11 +5247,14 @@ fn doAThing() ?*Foo { |
| 5241 | } | 5247 | } |
| 5242 | | 5248 | |
| 5243 | // do some stuff | 5249 | // do some stuff |
| 5244 | }</code></pre> | 5250 | }{#end_syntax_block#} |
| 5245 | <p> | 5251 | <p> |
| 5246 | In Zig you can accomplish the same thing: | 5252 | In Zig you can accomplish the same thing: |
| 5247 | </p> | 5253 | </p> |
| 5248 | {#code_begin|syntax#} | 5254 | {#code_begin|syntax|checking_null_in_zig#} |
| | 5255 | const Foo = struct{}; |
| | 5256 | fn doSomethingWithFoo(foo: *Foo) void { _ = foo; } |
| | 5257 | |
| 5249 | fn doAThing(optional_foo: ?*Foo) void { | 5258 | fn doAThing(optional_foo: ?*Foo) void { |
| 5250 | // do some stuff | 5259 | // do some stuff |
| 5251 | | 5260 | |
| ... | @@ -6111,7 +6120,7 @@ test "perform fn" { | ... | @@ -6111,7 +6120,7 @@ test "perform fn" { |
| 6111 | different code. In this example, the function {#syntax#}performFn{#endsyntax#} is generated three different times, | 6120 | different code. In this example, the function {#syntax#}performFn{#endsyntax#} is generated three different times, |
| 6112 | for the different values of {#syntax#}prefix_char{#endsyntax#} provided: | 6121 | for the different values of {#syntax#}prefix_char{#endsyntax#} provided: |
| 6113 | </p> | 6122 | </p> |
| 6114 | {#code_begin|syntax#} | 6123 | {#syntax_block|zig|performFn_1#} |
| 6115 | // From the line: | 6124 | // From the line: |
| 6116 | // expect(performFn('t', 1) == 6); | 6125 | // expect(performFn('t', 1) == 6); |
| 6117 | fn performFn(start_value: i32) i32 { | 6126 | fn performFn(start_value: i32) i32 { |
| ... | @@ -6120,8 +6129,8 @@ fn performFn(start_value: i32) i32 { | ... | @@ -6120,8 +6129,8 @@ fn performFn(start_value: i32) i32 { |
| 6120 | result = three(result); | 6129 | result = three(result); |
| 6121 | return result; | 6130 | return result; |
| 6122 | } | 6131 | } |
| 6123 | {#code_end#} | 6132 | {#end_syntax_block#} |
| 6124 | {#code_begin|syntax#} | 6133 | {#syntax_block|zig|performFn_2#} |
| 6125 | // From the line: | 6134 | // From the line: |
| 6126 | // expect(performFn('o', 0) == 1); | 6135 | // expect(performFn('o', 0) == 1); |
| 6127 | fn performFn(start_value: i32) i32 { | 6136 | fn performFn(start_value: i32) i32 { |
| ... | @@ -6129,15 +6138,15 @@ fn performFn(start_value: i32) i32 { | ... | @@ -6129,15 +6138,15 @@ fn performFn(start_value: i32) i32 { |
| 6129 | result = one(result); | 6138 | result = one(result); |
| 6130 | return result; | 6139 | return result; |
| 6131 | } | 6140 | } |
| 6132 | {#code_end#} | 6141 | {#end_syntax_block#} |
| 6133 | {#code_begin|syntax#} | 6142 | {#syntax_block|zig|performFn_3#} |
| 6134 | // From the line: | 6143 | // From the line: |
| 6135 | // expect(performFn('w', 99) == 99); | 6144 | // expect(performFn('w', 99) == 99); |
| 6136 | fn performFn(start_value: i32) i32 { | 6145 | fn performFn(start_value: i32) i32 { |
| 6137 | var result: i32 = start_value; | 6146 | var result: i32 = start_value; |
| 6138 | return result; | 6147 | return result; |
| 6139 | } | 6148 | } |
| 6140 | {#code_end#} | 6149 | {#end_syntax_block#} |
| 6141 | <p> | 6150 | <p> |
| 6142 | Note that this happens even in a debug build; in a release build these generated functions still | 6151 | Note that this happens even in a debug build; in a release build these generated functions still |
| 6143 | pass through rigorous LLVM optimizations. The important thing to note, however, is not that this | 6152 | pass through rigorous LLVM optimizations. The important thing to note, however, is not that this |
| ... | @@ -6367,11 +6376,11 @@ const Node = struct { | ... | @@ -6367,11 +6376,11 @@ const Node = struct { |
| 6367 | it works fine. | 6376 | it works fine. |
| 6368 | </p> | 6377 | </p> |
| 6369 | {#header_close#} | 6378 | {#header_close#} |
| 6370 | {#header_open|Case Study: printf in Zig#} | 6379 | {#header_open|Case Study: print in Zig#} |
| 6371 | <p> | 6380 | <p> |
| 6372 | Putting all of this together, let's see how {#syntax#}printf{#endsyntax#} works in Zig. | 6381 | Putting all of this together, let's see how {#syntax#}print{#endsyntax#} works in Zig. |
| 6373 | </p> | 6382 | </p> |
| 6374 | {#code_begin|exe|printf#} | 6383 | {#code_begin|exe|print#} |
| 6375 | const print = @import("std").debug.print; | 6384 | const print = @import("std").debug.print; |
| 6376 | | 6385 | |
| 6377 | const a_number: i32 = 1234; | 6386 | const a_number: i32 = 1234; |
| ... | @@ -6386,67 +6395,84 @@ pub fn main() void { | ... | @@ -6386,67 +6395,84 @@ pub fn main() void { |
| 6386 | Let's crack open the implementation of this and see how it works: | 6395 | Let's crack open the implementation of this and see how it works: |
| 6387 | </p> | 6396 | </p> |
| 6388 | | 6397 | |
| 6389 | {#code_begin|syntax#} | 6398 | {#code_begin|syntax|poc_print_fn#} |
| 6390 | /// Calls print and then flushes the buffer. | 6399 | const Writer = struct { |
| 6391 | pub fn printf(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void { | 6400 | /// Calls print and then flushes the buffer. |
| 6392 | const State = enum { | 6401 | pub fn print(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void { |
| 6393 | start, | 6402 | const State = enum { |
| 6394 | open_brace, | 6403 | start, |
| 6395 | close_brace, | 6404 | open_brace, |
| 6396 | }; | 6405 | close_brace, |
| 6397 | | 6406 | }; |
| 6398 | comptime var start_index: usize = 0; | | |
| 6399 | comptime var state = State.start; | | |
| 6400 | comptime var next_arg: usize = 0; | | |
| 6401 | | 6407 | |
| 6402 | inline for (format) |c, i| { | 6408 | comptime var start_index: usize = 0; |
| 6403 | switch (state) { | 6409 | comptime var state = State.start; |
| 6404 | State.start => switch (c) { | 6410 | comptime var next_arg: usize = 0; |
| 6405 | '{' => { | 6411 | |
| 6406 | if (start_index < i) try self.write(format[start_index..i]); | 6412 | inline for (format) |c, i| { |
| 6407 | state = State.open_brace; | 6413 | switch (state) { |
| 6408 | }, | 6414 | State.start => switch (c) { |
| 6409 | '}' => { | 6415 | '{' => { |
| 6410 | if (start_index < i) try self.write(format[start_index..i]); | 6416 | if (start_index < i) try self.write(format[start_index..i]); |
| 6411 | state = State.close_brace; | 6417 | state = State.open_brace; |
| 6412 | }, | 6418 | }, |
| 6413 | else => {}, | 6419 | '}' => { |
| 6414 | }, | 6420 | if (start_index < i) try self.write(format[start_index..i]); |
| 6415 | State.open_brace => switch (c) { | 6421 | state = State.close_brace; |
| 6416 | '{' => { | 6422 | }, |
| 6417 | state = State.start; | 6423 | else => {}, |
| 6418 | start_index = i; | | |
| 6419 | }, | 6424 | }, |
| 6420 | '}' => { | 6425 | State.open_brace => switch (c) { |
| 6421 | try self.printValue(args[next_arg]); | 6426 | '{' => { |
| 6422 | next_arg += 1; | 6427 | state = State.start; |
| 6423 | state = State.start; | 6428 | start_index = i; |
| 6424 | start_index = i + 1; | 6429 | }, |
| | 6430 | '}' => { |
| | 6431 | try self.printValue(args[next_arg]); |
| | 6432 | next_arg += 1; |
| | 6433 | state = State.start; |
| | 6434 | start_index = i + 1; |
| | 6435 | }, |
| | 6436 | 's' => { |
| | 6437 | continue; |
| | 6438 | }, |
| | 6439 | else => @compileError("Unknown format character: " ++ [1]u8{c}), |
| 6425 | }, | 6440 | }, |
| 6426 | else => @compileError("Unknown format character: " ++ c), | 6441 | State.close_brace => switch (c) { |
| 6427 | }, | 6442 | '}' => { |
| 6428 | State.close_brace => switch (c) { | 6443 | state = State.start; |
| 6429 | '}' => { | 6444 | start_index = i; |
| 6430 | state = State.start; | 6445 | }, |
| 6431 | start_index = i; | 6446 | else => @compileError("Single '}' encountered in format string"), |
| 6432 | }, | 6447 | }, |
| 6433 | else => @compileError("Single '}' encountered in format string"), | 6448 | } |
| 6434 | }, | | |
| 6435 | } | 6449 | } |
| 6436 | } | 6450 | comptime { |
| 6437 | comptime { | 6451 | if (args.len != next_arg) { |
| 6438 | if (args.len != next_arg) { | 6452 | @compileError("Unused arguments"); |
| 6439 | @compileError("Unused arguments"); | 6453 | } |
| | 6454 | if (state != State.start) { |
| | 6455 | @compileError("Incomplete format string: " ++ format); |
| | 6456 | } |
| 6440 | } | 6457 | } |
| 6441 | if (state != State.Start) { | 6458 | if (start_index < format.len) { |
| 6442 | @compileError("Incomplete format string: " ++ format); | 6459 | try self.write(format[start_index..format.len]); |
| 6443 | } | 6460 | } |
| | 6461 | try self.flush(); |
| 6444 | } | 6462 | } |
| 6445 | if (start_index < format.len) { | 6463 | |
| 6446 | try self.write(format[start_index..format.len]); | 6464 | fn write(self: *Writer, value: []const u8) !void { |
| | 6465 | _ = self; |
| | 6466 | _ = value; |
| 6447 | } | 6467 | } |
| 6448 | try self.flush(); | 6468 | pub fn printValue(self: *Writer, value: anytype) !void { |
| 6449 | } | 6469 | _ = self; |
| | 6470 | _ = value; |
| | 6471 | } |
| | 6472 | fn flush(self: *Writer) !void { |
| | 6473 | _ = self; |
| | 6474 | } |
| | 6475 | }; |
| 6450 | {#code_end#} | 6476 | {#code_end#} |
| 6451 | <p> | 6477 | <p> |
| 6452 | This is a proof of concept implementation; the actual function in the standard library has more | 6478 | This is a proof of concept implementation; the actual function in the standard library has more |
| ... | @@ -6459,8 +6485,8 @@ pub fn printf(self: *Writer, comptime format: []const u8, args: anytype) anyerro | ... | @@ -6459,8 +6485,8 @@ pub fn printf(self: *Writer, comptime format: []const u8, args: anytype) anyerro |
| 6459 | When this function is analyzed from our example code above, Zig partially evaluates the function | 6485 | When this function is analyzed from our example code above, Zig partially evaluates the function |
| 6460 | and emits a function that actually looks like this: | 6486 | and emits a function that actually looks like this: |
| 6461 | </p> | 6487 | </p> |
| 6462 | {#code_begin|syntax#} | 6488 | {#syntax_block|zig|Emitted print Function#} |
| 6463 | pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void { | 6489 | pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void { |
| 6464 | try self.write("here is a string: '"); | 6490 | try self.write("here is a string: '"); |
| 6465 | try self.printValue(arg0); | 6491 | try self.printValue(arg0); |
| 6466 | try self.write("' here is a number: "); | 6492 | try self.write("' here is a number: "); |
| ... | @@ -6468,28 +6494,46 @@ pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void { | ... | @@ -6468,28 +6494,46 @@ pub fn printf(self: *Writer, arg0: i32, arg1: []const u8) !void { |
| 6468 | try self.write("\n"); | 6494 | try self.write("\n"); |
| 6469 | try self.flush(); | 6495 | try self.flush(); |
| 6470 | } | 6496 | } |
| 6471 | {#code_end#} | 6497 | {#end_syntax_block#} |
| 6472 | <p> | 6498 | <p> |
| 6473 | {#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending | 6499 | {#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending |
| 6474 | on the type: | 6500 | on the type: |
| 6475 | </p> | 6501 | </p> |
| 6476 | {#code_begin|syntax#} | 6502 | {#code_begin|syntax|poc_printValue_fn#} |
| 6477 | pub fn printValue(self: *Writer, value: anytype) !void { | 6503 | const Writer = struct { |
| 6478 | switch (@typeInfo(@TypeOf(value))) { | 6504 | pub fn printValue(self: *Writer, value: anytype) !void { |
| 6479 | .Int => { | 6505 | switch (@typeInfo(@TypeOf(value))) { |
| 6480 | return self.printInt(T, value); | 6506 | .Int => { |
| 6481 | }, | 6507 | return self.writeInt(value); |
| 6482 | .Float => { | 6508 | }, |
| 6483 | return self.printFloat(T, value); | 6509 | .Float => { |
| 6484 | }, | 6510 | return self.writeFloat(value); |
| 6485 | else => { | 6511 | }, |
| 6486 | @compileError("Unable to print type '" ++ @typeName(T) ++ "'"); | 6512 | .Pointer => { |
| 6487 | }, | 6513 | return self.write(value); |
| | 6514 | }, |
| | 6515 | else => { |
| | 6516 | @compileError("Unable to print type '" ++ @typeName(@TypeOf(value)) ++ "'"); |
| | 6517 | }, |
| | 6518 | } |
| 6488 | } | 6519 | } |
| 6489 | } | 6520 | |
| | 6521 | fn write(self: *Writer, value: []const u8) !void { |
| | 6522 | _ = self; |
| | 6523 | _ = value; |
| | 6524 | } |
| | 6525 | fn writeInt(self: *Writer, value: anytype) !void { |
| | 6526 | _ = self; |
| | 6527 | _ = value; |
| | 6528 | } |
| | 6529 | fn writeFloat(self: *Writer, value: anytype) !void { |
| | 6530 | _ = self; |
| | 6531 | _ = value; |
| | 6532 | } |
| | 6533 | }; |
| 6490 | {#code_end#} | 6534 | {#code_end#} |
| 6491 | <p> | 6535 | <p> |
| 6492 | And now, what happens if we give too many arguments to {#syntax#}printf{#endsyntax#}? | 6536 | And now, what happens if we give too many arguments to {#syntax#}print{#endsyntax#}? |
| 6493 | </p> | 6537 | </p> |
| 6494 | {#code_begin|test_err|Unused argument in 'here is a string: '{s}' here is a number: {}#} | 6538 | {#code_begin|test_err|Unused argument in 'here is a string: '{s}' here is a number: {}#} |
| 6495 | const print = @import("std").debug.print; | 6539 | const print = @import("std").debug.print; |
| ... | @@ -6497,7 +6541,7 @@ const print = @import("std").debug.print; | ... | @@ -6497,7 +6541,7 @@ const print = @import("std").debug.print; |
| 6497 | const a_number: i32 = 1234; | 6541 | const a_number: i32 = 1234; |
| 6498 | const a_string = "foobar"; | 6542 | const a_string = "foobar"; |
| 6499 | | 6543 | |
| 6500 | test "printf too many arguments" { | 6544 | test "print too many arguments" { |
| 6501 | print("here is a string: '{s}' here is a number: {}\n", .{ | 6545 | print("here is a string: '{s}' here is a number: {}\n", .{ |
| 6502 | a_string, | 6546 | a_string, |
| 6503 | a_number, | 6547 | a_number, |
| ... | @@ -6512,7 +6556,7 @@ test "printf too many arguments" { | ... | @@ -6512,7 +6556,7 @@ test "printf too many arguments" { |
| 6512 | Zig doesn't care whether the format argument is a string literal, | 6556 | Zig doesn't care whether the format argument is a string literal, |
| 6513 | only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}: | 6557 | only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}: |
| 6514 | </p> | 6558 | </p> |
| 6515 | {#code_begin|exe|printf#} | 6559 | {#code_begin|exe|print#} |
| 6516 | const print = @import("std").debug.print; | 6560 | const print = @import("std").debug.print; |
| 6517 | | 6561 | |
| 6518 | const a_number: i32 = 1234; | 6562 | const a_number: i32 = 1234; |
| ... | @@ -7401,9 +7445,11 @@ fn add(a: i32, b: i32) i32 { | ... | @@ -7401,9 +7445,11 @@ fn add(a: i32, b: i32) i32 { |
| 7401 | {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The | 7445 | {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The |
| 7402 | {#syntax#}CallOptions{#endsyntax#} struct is reproduced here: | 7446 | {#syntax#}CallOptions{#endsyntax#} struct is reproduced here: |
| 7403 | </p> | 7447 | </p> |
| 7404 | {#code_begin|syntax#} | 7448 | {#syntax_block|zig|builtin.CallOptions struct#} |
| 7405 | pub const CallOptions = struct { | 7449 | pub const CallOptions = struct { |
| 7406 | modifier: Modifier = .auto, | 7450 | modifier: Modifier = .auto, |
| | 7451 | |
| | 7452 | /// Only valid when `Modifier` is `Modifier.async_kw`. |
| 7407 | stack: ?[]align(std.Target.stack_align) u8 = null, | 7453 | stack: ?[]align(std.Target.stack_align) u8 = null, |
| 7408 | | 7454 | |
| 7409 | pub const Modifier = enum { | 7455 | pub const Modifier = enum { |
| ... | @@ -7440,7 +7486,7 @@ pub const CallOptions = struct { | ... | @@ -7440,7 +7486,7 @@ pub const CallOptions = struct { |
| 7440 | compile_time, | 7486 | compile_time, |
| 7441 | }; | 7487 | }; |
| 7442 | }; | 7488 | }; |
| 7443 | {#code_end#} | 7489 | {#end_syntax_block#} |
| 7444 | {#header_close#} | 7490 | {#header_close#} |
| 7445 | | 7491 | |
| 7446 | {#header_open|@cDefine#} | 7492 | {#header_open|@cDefine#} |
| ... | @@ -7554,7 +7600,7 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v | ... | @@ -7554,7 +7600,7 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_v |
| 7554 | This function performs a weak atomic compare exchange operation. It's the equivalent of this code, | 7600 | This function performs a weak atomic compare exchange operation. It's the equivalent of this code, |
| 7555 | except atomic: | 7601 | except atomic: |
| 7556 | </p> | 7602 | </p> |
| 7557 | {#code_begin|syntax#} | 7603 | {#syntax_block|zig|cmpxchgWeakButNotAtomic#} |
| 7558 | fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T { | 7604 | fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T { |
| 7559 | const old_value = ptr.*; | 7605 | const old_value = ptr.*; |
| 7560 | if (old_value == expected_value and usuallyTrueButSometimesFalse()) { | 7606 | if (old_value == expected_value and usuallyTrueButSometimesFalse()) { |
| ... | @@ -7564,7 +7610,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val | ... | @@ -7564,7 +7610,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 7564 | return old_value; | 7610 | return old_value; |
| 7565 | } | 7611 | } |
| 7566 | } | 7612 | } |
| 7567 | {#code_end#} | 7613 | {#end_syntax_block#} |
| 7568 | <p> | 7614 | <p> |
| 7569 | If you are using cmpxchg in a loop, the sporadic failure will be no problem, and {#syntax#}cmpxchgWeak{#endsyntax#} | 7615 | If you are using cmpxchg in a loop, the sporadic failure will be no problem, and {#syntax#}cmpxchgWeak{#endsyntax#} |
| 7570 | is the better choice, because it can be implemented more efficiently in machine instructions. | 7616 | is the better choice, because it can be implemented more efficiently in machine instructions. |
| ... | @@ -10159,7 +10205,7 @@ pub fn main() void { | ... | @@ -10159,7 +10205,7 @@ pub fn main() void { |
| 10159 | This expression is evaluated at compile-time and is used to control | 10205 | This expression is evaluated at compile-time and is used to control |
| 10160 | preprocessor directives and include multiple <code class="file">.h</code> files: | 10206 | preprocessor directives and include multiple <code class="file">.h</code> files: |
| 10161 | </p> | 10207 | </p> |
| 10162 | {#code_begin|syntax#} | 10208 | {#syntax_block|zig|@cImport Expression#} |
| 10163 | const builtin = @import("builtin"); | 10209 | const builtin = @import("builtin"); |
| 10164 | | 10210 | |
| 10165 | const c = @cImport({ | 10211 | const c = @cImport({ |
| ... | @@ -10173,7 +10219,7 @@ const c = @cImport({ | ... | @@ -10173,7 +10219,7 @@ const c = @cImport({ |
| 10173 | } | 10219 | } |
| 10174 | @cInclude("soundio.h"); | 10220 | @cInclude("soundio.h"); |
| 10175 | }); | 10221 | }); |
| 10176 | {#code_end#} | 10222 | {#end_syntax_block#} |
| 10177 | {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#} | 10223 | {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#} |
| 10178 | {#header_close#} | 10224 | {#header_close#} |
| 10179 | | 10225 | |