authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-11-26 15:03:24-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-11-26 15:03:24-08:00
log3ce6de87657e67da358e9c7869a87c61cc9f1305
tree6ed26d60882e80967afe849abfc172bd4ae37ed2
parent11bf2d92de5f335d4e07358d99f26fae08493f12

revert langref section "common errdefer slip ups"

This does not belong in the language reference. reverts 91a88a789ffa80ebb57c77ae0fe37594276e3707

5 files changed, 0 insertions(+), 178 deletions(-)

doc/langref.html.in-26
......@@ -3052,32 +3052,6 @@ fn createFoo(param: i32) !Foo {
30523052 The {#syntax#}errdefer{#endsyntax#} statement can optionally capture the error:
30533053 </p>
30543054 {#code|test_errdefer_capture.zig#}
3055 {#header_close#}
3056 {#header_open|Common errdefer Slip-Ups#}
3057 <p>
3058 It should be noted that {#syntax#}errdefer{#endsyntax#} statements only last until the end of the block
3059 they are written in, and therefore are not run if an error is returned outside of that block:
3060 </p>
3061 {#code|test_errdefer_slip_ups.zig#}
3062
3063 <p>
3064 To ensure that {#syntax#}deallocateFoo{#endsyntax#} is properly called
3065 when returning an error, you must add an {#syntax#}errdefer{#endsyntax#} outside of the block:
3066 </p>
3067 {#code|test_errdefer_block.zig#}
3068
3069 <p>
3070 The fact that errdefers only last for the block they are declared in is
3071 especially important when using loops:
3072 </p>
3073 {#code|test_errdefer_loop_leak.zig#}
3074
3075 <p>
3076 Special care must be taken with code that allocates in a loop
3077 to make sure that no memory is leaked when returning an error:
3078 </p>
3079 {#code|test_errdefer_loop.zig#}
3080
30813055 {#header_close#}
30823056 <p>
30833057 A couple of other tidbits about error handling:
doc/langref/test_errdefer_block.zig deleted-42
......@@ -1,42 +0,0 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4const Foo = struct {
5 data: u32,
6};
7
8fn tryToAllocateFoo(allocator: Allocator) !*Foo {
9 return allocator.create(Foo);
10}
11
12fn deallocateFoo(allocator: Allocator, foo: *Foo) void {
13 allocator.destroy(foo);
14}
15
16fn getFooData() !u32 {
17 return 666;
18}
19
20fn createFoo(allocator: Allocator, param: i32) !*Foo {
21 const foo = getFoo: {
22 var foo = try tryToAllocateFoo(allocator);
23 errdefer deallocateFoo(allocator, foo);
24
25 foo.data = try getFooData();
26
27 break :getFoo foo;
28 };
29 // This lasts for the rest of the function
30 errdefer deallocateFoo(allocator, foo);
31
32 // Error is now properly handled by errdefer
33 if (param > 1337) return error.InvalidParam;
34
35 return foo;
36}
37
38test "createFoo" {
39 try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468));
40}
41
42// test
doc/langref/test_errdefer_loop.zig deleted-36
......@@ -1,36 +0,0 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4const Foo = struct { data: *u32 };
5
6fn getData() !u32 {
7 return 666;
8}
9
10fn genFoos(allocator: Allocator, num: usize) ![]Foo {
11 const foos = try allocator.alloc(Foo, num);
12 errdefer allocator.free(foos);
13
14 // Used to track how many foos have been initialized
15 // (including their data being allocated)
16 var num_allocated: usize = 0;
17 errdefer for (foos[0..num_allocated]) |foo| {
18 allocator.destroy(foo.data);
19 };
20 for (foos, 0..) |*foo, i| {
21 foo.data = try allocator.create(u32);
22 num_allocated += 1;
23
24 if (i >= 3) return error.TooManyFoos;
25
26 foo.data.* = try getData();
27 }
28
29 return foos;
30}
31
32test "genFoos" {
33 try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5));
34}
35
36// test
doc/langref/test_errdefer_loop_leak.zig deleted-32
......@@ -1,32 +0,0 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4const Foo = struct { data: *u32 };
5
6fn getData() !u32 {
7 return 666;
8}
9
10fn genFoos(allocator: Allocator, num: usize) ![]Foo {
11 const foos = try allocator.alloc(Foo, num);
12 errdefer allocator.free(foos);
13
14 for (foos, 0..) |*foo, i| {
15 foo.data = try allocator.create(u32);
16 // This errdefer does not last between iterations
17 errdefer allocator.destroy(foo.data);
18
19 // The data for the first 3 foos will be leaked
20 if (i >= 3) return error.TooManyFoos;
21
22 foo.data.* = try getData();
23 }
24
25 return foos;
26}
27
28test "genFoos" {
29 try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5));
30}
31
32// test_error=3 errors were logged
doc/langref/test_errdefer_slip_ups.zig deleted-42
......@@ -1,42 +0,0 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4const Foo = struct {
5 data: u32,
6};
7
8fn tryToAllocateFoo(allocator: Allocator) !*Foo {
9 return allocator.create(Foo);
10}
11
12fn deallocateFoo(allocator: Allocator, foo: *Foo) void {
13 allocator.destroy(foo);
14}
15
16fn getFooData() !u32 {
17 return 666;
18}
19
20fn createFoo(allocator: Allocator, param: i32) !*Foo {
21 const foo = getFoo: {
22 var foo = try tryToAllocateFoo(allocator);
23 errdefer deallocateFoo(allocator, foo); // Only lasts until the end of getFoo
24
25 // Calls deallocateFoo on error
26 foo.data = try getFooData();
27
28 break :getFoo foo;
29 };
30
31 // Outside of the scope of the errdefer, so
32 // deallocateFoo will not be called here
33 if (param > 1337) return error.InvalidParam;
34
35 return foo;
36}
37
38test "createFoo" {
39 try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468));
40}
41
42// test_error=1 tests leaked memory