| ... | ... | @@ -5335,6 +5335,179 @@ fn createFoo(param: i32) !Foo { |
| 5335 | 5335 | is covered. The deallocation code is always directly following the allocation code. |
| 5336 | 5336 | </p> |
| 5337 | 5337 | {#header_close#} |
| 5338 | {#header_open|Common errdefer Slip-Ups#} |
| 5339 | <p> |
| 5340 | It should be noted that {#syntax#}errdefer{#endsyntax#} statements only last until the end of the block |
| 5341 | they are written in, and therefore are not run if an error is returned outside of that block: |
| 5342 | </p> |
| 5343 | {#code_begin|test_err|1 tests leaked memory#} |
| 5344 | const std = @import("std"); |
| 5345 | const Allocator = std.mem.Allocator; |
| 5346 | |
| 5347 | const Foo = struct { |
| 5348 | data: u32, |
| 5349 | }; |
| 5350 | |
| 5351 | fn tryToAllocateFoo(allocator: Allocator) !*Foo { |
| 5352 | return allocator.create(Foo); |
| 5353 | } |
| 5354 | |
| 5355 | fn deallocateFoo(allocator: Allocator, foo: *Foo) void { |
| 5356 | allocator.destroy(foo); |
| 5357 | } |
| 5358 | |
| 5359 | fn getFooData() !u32 { |
| 5360 | return 666; |
| 5361 | } |
| 5362 | |
| 5363 | fn createFoo(allocator: Allocator, param: i32) !*Foo { |
| 5364 | const foo = getFoo: { |
| 5365 | var foo = try tryToAllocateFoo(allocator); |
| 5366 | errdefer deallocateFoo(allocator, foo); // Only lasts until the end of getFoo |
| 5367 | |
| 5368 | // Calls deallocateFoo on error |
| 5369 | foo.data = try getFooData(); |
| 5370 | |
| 5371 | break :getFoo foo; |
| 5372 | }; |
| 5373 | |
| 5374 | // Outside of the scope of the errdefer, so |
| 5375 | // deallocateFoo will not be called here |
| 5376 | if (param > 1337) return error.InvalidParam; |
| 5377 | |
| 5378 | return foo; |
| 5379 | } |
| 5380 | |
| 5381 | test "createFoo" { |
| 5382 | try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468)); |
| 5383 | } |
| 5384 | {#code_end#} |
| 5385 | <p> |
| 5386 | To ensure that {#syntax#}deallocateFoo{#endsyntax#} is properly called |
| 5387 | when returning an error, you must add an {#syntax#}errdefer{#endsyntax#} outside of the block: |
| 5388 | {#code_begin|test|test_errdefer_block#} |
| 5389 | const std = @import("std"); |
| 5390 | const Allocator = std.mem.Allocator; |
| 5391 | |
| 5392 | const Foo = struct { |
| 5393 | data: u32, |
| 5394 | }; |
| 5395 | |
| 5396 | fn tryToAllocateFoo(allocator: Allocator) !*Foo { |
| 5397 | return allocator.create(Foo); |
| 5398 | } |
| 5399 | |
| 5400 | fn deallocateFoo(allocator: Allocator, foo: *Foo) void { |
| 5401 | allocator.destroy(foo); |
| 5402 | } |
| 5403 | |
| 5404 | fn getFooData() !u32 { |
| 5405 | return 666; |
| 5406 | } |
| 5407 | |
| 5408 | fn createFoo(allocator: Allocator, param: i32) !*Foo { |
| 5409 | const foo = getFoo: { |
| 5410 | var foo = try tryToAllocateFoo(allocator); |
| 5411 | errdefer deallocateFoo(allocator, foo); |
| 5412 | |
| 5413 | foo.data = try getFooData(); |
| 5414 | |
| 5415 | break :getFoo foo; |
| 5416 | }; |
| 5417 | // This lasts for the rest of the function |
| 5418 | errdefer deallocateFoo(allocator, foo); |
| 5419 | |
| 5420 | // Error is now properly handled by errdefer |
| 5421 | if (param > 1337) return error.InvalidParam; |
| 5422 | |
| 5423 | return foo; |
| 5424 | } |
| 5425 | |
| 5426 | test "createFoo" { |
| 5427 | try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468)); |
| 5428 | } |
| 5429 | {#code_end#} |
| 5430 | <p> |
| 5431 | The fact that errdefers only last for the block they are declared in is |
| 5432 | especially important when using loops: |
| 5433 | </p> |
| 5434 | {#code_begin|test_err|3 errors were logged#} |
| 5435 | const std = @import("std"); |
| 5436 | const Allocator = std.mem.Allocator; |
| 5437 | |
| 5438 | const Foo = struct { |
| 5439 | data: *u32 |
| 5440 | }; |
| 5441 | |
| 5442 | fn getData() !u32 { |
| 5443 | return 666; |
| 5444 | } |
| 5445 | |
| 5446 | fn genFoos(allocator: Allocator, num: usize) ![]Foo { |
| 5447 | var foos = try allocator.alloc(Foo, num); |
| 5448 | errdefer allocator.free(foos); |
| 5449 | |
| 5450 | for(foos) |*foo, i| { |
| 5451 | foo.data = try allocator.create(u32); |
| 5452 | // This errdefer does not last between iterations |
| 5453 | errdefer allocator.destroy(foo.data); |
| 5454 | |
| 5455 | // The data for the first 3 foos will be leaked |
| 5456 | if(i >= 3) return error.TooManyFoos; |
| 5457 | |
| 5458 | foo.data.* = try getData(); |
| 5459 | } |
| 5460 | |
| 5461 | return foos; |
| 5462 | } |
| 5463 | |
| 5464 | test "genFoos" { |
| 5465 | try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5)); |
| 5466 | } |
| 5467 | {#code_end#} |
| 5468 | <p> |
| 5469 | Special care must be taken with code that allocates in a loop |
| 5470 | to make sure that no memory is leaked when returning an error: |
| 5471 | </p> |
| 5472 | {#code_begin|test|test_errdefer_loop#} |
| 5473 | const std = @import("std"); |
| 5474 | const Allocator = std.mem.Allocator; |
| 5475 | |
| 5476 | const Foo = struct { |
| 5477 | data: *u32 |
| 5478 | }; |
| 5479 | |
| 5480 | fn getData() !u32 { |
| 5481 | return 666; |
| 5482 | } |
| 5483 | |
| 5484 | fn genFoos(allocator: Allocator, num: usize) ![]Foo { |
| 5485 | var foos = try allocator.alloc(Foo, num); |
| 5486 | errdefer allocator.free(foos); |
| 5487 | |
| 5488 | // Used to track how many foos have been initialized |
| 5489 | // (including their data being allocated) |
| 5490 | var num_allocated: usize = 0; |
| 5491 | errdefer for(foos[0..num_allocated]) |foo| { |
| 5492 | allocator.destroy(foo.data); |
| 5493 | }; |
| 5494 | for(foos) |*foo, i| { |
| 5495 | foo.data = try allocator.create(u32); |
| 5496 | num_allocated += 1; |
| 5497 | |
| 5498 | if(i >= 3) return error.TooManyFoos; |
| 5499 | |
| 5500 | foo.data.* = try getData(); |
| 5501 | } |
| 5502 | |
| 5503 | return foos; |
| 5504 | } |
| 5505 | |
| 5506 | test "genFoos" { |
| 5507 | try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5)); |
| 5508 | } |
| 5509 | {#code_end#} |
| 5510 | {#header_close#} |
| 5338 | 5511 | <p> |
| 5339 | 5512 | A couple of other tidbits about error handling: |
| 5340 | 5513 | </p> |