-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
add getLastPtr and getLastOrNullPtr functions to ArrayLists #24379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
I'm not sure that this is a good idea. Pointers to ArrayList elements can be invalidated whenever the list needs to grow in capacity. I feel that the introduction of these functions will introduce bugs that are hard to track down. |
That's true, |
I appreciate the response, yet I think the context in which these functions are used may be different. I have used |
The case I had in mind when adding these functions was for ArrayLists of structs that have a function modifying themselves: |
I think it's not a needed change, but perhaps one question would be to see what code is improved by this change. For example, I believe this diff is equivalent using diff --git a/tools/incr-check.zig b/tools/incr-check.zig
index 08b8a21e3b..4c47a786ee 100644
--- a/tools/incr-check.zig
+++ b/tools/incr-check.zig
@@ -729,7 +729,7 @@ const Case = struct {
});
} else if (std.mem.eql(u8, key, "update")) {
if (updates.items.len > 0) {
- const last_update = &updates.items[updates.items.len - 1];
+ const last_update = updates.getLastPtr();
last_update.changes = try changes.toOwnedSlice(arena);
last_update.deletes = try deletes.toOwnedSlice(arena);
}
@@ -769,7 +769,7 @@ const Case = struct {
try deletes.append(arena, val);
} else if (std.mem.eql(u8, key, "expect_stdout")) {
if (updates.items.len == 0) fatal("line {d}: expect directive before update", .{line_n});
- const last_update = &updates.items[updates.items.len - 1];
+ const last_update = updates.getLastPtr();
if (last_update.outcome != .unknown) fatal("line {d}: conflicting expect directive", .{line_n});
last_update.outcome = .{
.stdout = std.zig.string_literal.parseAlloc(arena, val) catch |err| {
@@ -778,7 +778,7 @@ const Case = struct {
};
} else if (std.mem.eql(u8, key, "expect_error")) {
if (updates.items.len == 0) fatal("line {d}: expect directive before update", .{line_n});
- const last_update = &updates.items[updates.items.len - 1];
+ const last_update = updates.getLastPtr();
if (last_update.outcome != .unknown) fatal("line {d}: conflicting expect directive", .{line_n});
var errors: std.ArrayListUnmanaged(ExpectedError) = .empty;
@@ -831,7 +831,7 @@ const Case = struct {
}
if (changes.items.len > 0) {
- const last_update = &updates.items[updates.items.len - 1];
+ const last_update = updates.getLastPtr();
last_update.changes = changes.items; // arena so no need for toOwnedSlice
last_update.deletes = deletes.items;
} Here are some other possibilities, but i didn't verify them all.
It's not that many places and the code looks fine to me as-is, but one compelling reason might be to reduce the risk of typos in miscalculating the last item index... |
Replying to slooWne: Interesting case - I had not considered the argument of a structure which requires a pointer to itself. In fact, how often does this ever occur? I don't remember a time I've ever needed to do that. As for the const last_element = &list.items[list.items.len - 1];
// ...
last_element.someOperation(); I intended to communicate this: list.items[list.items.len - 1].someOperation(); Or even this: const last_index = list.items.len - 1;
list.items[last_index].someOperation(); As for the comment made by mrjbq7: I don't believe that the change would impact code generation, especially for an optimizing compiler. The change is trivial enough that either way is likely identical, though I'd love to be proven wrong. I don't think that miscalculating the last index (via a typo) is significant enough to warrant a special purposed function though. In the end we are discussing nuances about a function that makes code slightly more concise. Is Zig the type of language where we want to abstract single (& simple) lines of code behind a function call? I don't think so. I argue that the information conveyed in either case is the same (and may be more clear if written explicitly). EDIT: I read your comment wrong mrjbq7 - I see now you were talking about the code, and not code generation. In that case, I am in agreement. |
I think before thinking about adding different variations of the |
I guess it's like pop, it's for stack like usage of arrayLists |
as getLast exists, it would be useful to have a version that also returns a pointer to the last element.