开发者生态
morning
Zig:ArrayList 的指针稳定性
摘要
This page contains a curated list of recent changes to main branch Zig. August 27, 2026 Author: Robbie Lyman Pointer Stability Locks were added to std ’s Hash Map containers in 2024 . A pull request i...
std
const
the
Context
history
ctx
this
input
lines
ArrayList
2026-08-31
1 阅读
约10分钟阅读
tosh
字号:
此页面包含主分支 Zig 的最新更改的精选列表。 2026 年 8 月 27 日 作者:Robbie Lyman 指针稳定性锁于 2024 年被添加到 std 的哈希映射容器中。 Leo Emar-Kar 最初于 2025 年提出的拉取请求现在将这种确保内存安全的技术引入了 std.ArrayList 。要在代码中使用此功能,请在首次存储指向 ArrayList 支持的元素或元素切片的指针时添加对 lockPointers() 的调用,并在不再需要这些指针时调用unlockPointers()。这是一个有点人为的例子。假设我们正在管理两个 ArrayList,其中一个在内存中保存某些输入的内容,而另一个则存储感兴趣的块;也许每一行。这是这个过程的一个版本,有一个错误;看看你是否能发现它。 const std = @import(“std”); const Context = struct { 历史 : std . ArrayList(u8),行:std。 ArrayList([] const u8),fn parse(ctx:*Context,分配器:std.mem.Allocator,输入:[]const u8)! void { const slice = 尝试 ctx 。历史 。 addManyAsSlice( 分配器 , 输入 .len ) ; @memcpy(切片,输入); var it = std .内存。 tokenizeScalar(u8,切片,'\n'); while ( it . next ( ) ) | while ( it . next ( ) ) | while ( it . next ( ) )线 | { 尝试 ctx 。线。追加(分配器,行); } } } ;您发现错误了吗?问题是 Context.lines.items 的元素取决于 Context.history.items 的位置,但如果 Context.history 需要增长超出其当前容量,则该位置可能会更改。这是该错误的重现: test "Context.parse" { const input = "I'm first! \n " ; const input_two = \\但是这个文本 \\juuuuuuuuuuuuuuuuuuuuuuuuust 足够长,以至于它 \\ 引起了一个问题! \\问题可能是我们的段错误!遇到这种情况一点也不有趣。 ; var ctx : 上下文 = . {。历史=.空的 , 。行=.空的 , } ;常量 gpa = 标准 .测试。分配器;推迟 ctx 。历史 。去初始化(gpa);推迟 ctx 。线。去初始化(gpa);尝试 ctx 。解析(gpa,输入);尝试 ctx 。解析(gpa,input_two);尝试标准。测试。 ExpectEqualStrings("我是第一个!", ctx.lines.items[0]);如果我使用 zig test 运行此代码,我会得到以下输出(再加上更多)。 ====== 期望这个输出: ========= 我是第一个!␃ ======== 相反发现了这个: ========= UUUUUUUUUU␃ ======================================== 第 1 行出现第一个差异: 预期:我是第一个! ^ ('\x49') 发现: UUUUUUUUUU ^ ('\x55') 1/1 blah.test.Context.parse...FAIL (TestExpectedEqual) 不太好,对吧?这确实告诉我们,我们有一个错误,但是根据您调试内存问题的舒适程度(以及您选择的分配器,这将改变错误的表现方式!),您可能会在发现修复之前迷失相当长一段时间。由于我们在测试中第一次调用 parse 之后存储了指针,如果我们进行此更改会发生什么?尝试 ctx.parse(gpa, 输入); + ctx.history.lockPointers(); + defer ctx.history.unlockPointers();尝试 ctx.parse(gpa, input_two);尝试 std.testing.expectEqualStrings("我是第一个!", ctx.lines.items[0]);我们对堆栈跟踪感到恐慌,它向我们展示了我们关于指针稳定性的假设被违反的地方!线程 3023222 恐慌:在断言(测试)中达到无法访问的代码 /Users/robbie/bin/lib/std/debug.zig:442:14:0x102d2506f if (!ok) 无法访问; // 断言失败 ^ /Users/robbie/bin/lib/std/debug.zig:1880:15: 0x102d31ef7 在assertUnlocked (test)中assert(l.state == .unlocked); ^ /Users/robbie/bin/lib/std/array_list.zig:1348:50: 0x102e3ced7 insureTotalCapacityPrecise (测试) self.pointer_stability.assertUnlocked(); ^ /Users/robbie/bin/lib/std/array_list.zig:1341:51:ensureTotalCapacity(测试)中的0x102e3cdff返回self.ensureTotalCapacityPrecise(gpa,growCapacity(new_capacity)); ^ /Users/robbie/bin/lib/std/array_list.zig:1237:41: 0x102e4e5c3 调整大小(测试)尝试 self.ensureTotalCapacity(gpa, new_len); ^ /Users/robbie/bin/lib/std/array_list.zig:1461:28: 0x102e4e40f in addManyAsSlice (测试) 尝试 self.resize(gpa, 尝试 addOrOom(self.items.len, n)); ^ /Users/robbie/src/advent-of-code/2024/blah.zig:8:51: 0x102e4dc1f 解析(测试) const ptr = 尝试 ctx.history.addManyAsSlice(allocator, input.len); ^ /Users/robbie/src/advent-of-code/2024/blah.zig:35:18: test.Context.parse 中的 0x102e4e167 (测试)尝试 ctx.parse(gpa, input_two);很好,这已经是一个很大的帮助了:现在我可以看到,除了逻辑问题之外,我还应该将内存安全问题视为测试失败的可能原因。显然这个例子有些人为,但我确实发现自己在实际代码中使用了 std.ArrayList 作为这种类型的后备存
这篇文章对您有帮助吗?
订阅66必读
每日精选科技资讯,直达你的邮箱