#struggle understanding hashmap.get() logic
1 messages · Page 1 of 1 (latest)
the map could be on the stack and the contents on the heap (common), or the map could be on the heap and the contents could be allocated from the stack (uncommon but possible), or both or neither
get simply returns a copy of the value, getPtr returns a pointer to a value, which would become invalidated if you cause the hashmap to reallocate
if you append to a copy of an arraylist, while the original still exists, you invalidate the original
I think you have a muddy idea of what "cache" means
const std = @import("std");
test "pointer V" {
const ally = std.testing.allocator;
var map: std.AutoHashMapUnmanaged(u32, []u8) = .empty;
defer {
var it = map.iterator();
while (it.next()) |entry|
ally.free(entry.value_ptr.*);
map.deinit(ally);
}
try map.put(ally, 1, try ally.dupe(u8, "hello"));
const get = map.get(1).?;
get[0] = 'H';
try std.testing.expectEqualStrings("Hello", map.get(1).?);
const getptr = map.getPtr(1).?;
getptr.*[0] = 'h';
try std.testing.expectEqualStrings("hello", map.get(1).?);
}
test "value V" {
const ally = std.testing.allocator;
var map: std.AutoHashMapUnmanaged(u32, [5]u8) = .empty;
defer map.deinit(ally);
try map.put(ally, 1, "hello".*);
var get = map.get(1).?;
get[0] = 'H';
try std.testing.expectEqualStrings("hello", &map.get(1).?);
const getptr = map.getPtr(1).?;
getptr.*[0] = 'H';
try std.testing.expectEqualStrings("Hello", &map.get(1).?);
}
in the first case there's no difference because you get a slice either way, which points to the same memory that you mutate by either method.
in the second case there's a difference because you get a copy with one method but get a pointer to a value held by the hashmap in the other method.
good example, this i can understand, the issue i have is when dynamically allocated items are the hashmap values
such as a arrayList
that it's dynamically allocated is not really the issue. You could back an ArrayList with the stack using the appropriate allocator.
the issue is that an ArrayList contains a pointer, so whether you get a copy of the struct containing the pointer, or get a pointer to the struct containing the pointer, that inner pointer with the data you care about is the same.
but, an ArrayList isn't just that pointer. Changes to capacity could be lost if you make those only to a copy.
oh so in fact if i expand the arraylist, it does get expanded in the original location but the capacity wont get updated
is this right?
i blv its what i was missing
sry im bad at explaining lol
A simple rule is that you have to know what you're dealing with to use .get(), and if you don't want to, you're safer with .getPTr
i think it makes sense now, ty for the explainations 🙏
this is correct, yes
except it can go further: the original pointer itself may be invalidated, since realloc may return a different pointer if the original could not be resized
but you have the right concept
alright yea its very clear now
const std = @import("std");
const Person = struct {
name: []u8,
age: u128, // optimism
};
test {
const ally = std.testing.allocator;
var map: std.AutoHashMapUnmanaged(u32, Person) = .empty;
defer {
var it = map.iterator();
while (it.next()) |entry|
ally.free(entry.value_ptr.name);
map.deinit(ally);
}
try map.put(ally, 1, .{
.name = try ally.dupe(u8, "Alice"),
.age = 20,
});
// big hint: this can't be const
var get = map.get(1).?;
get.age += 1;
get.name[0] = 'a';
// no change:
try std.testing.expectEqual(20, map.get(1).?.age);
// change:
try std.testing.expectEqualStrings("alice", map.get(1).?.name);
const getptr = map.getPtr(1).?;
getptr.age += 1;
get.name[0] = 'A';
// change:
try std.testing.expectEqual(21, map.get(1).?.age);
// change:
try std.testing.expectEqualStrings("Alice", map.get(1).?.name);
}
name gets modified by both methods because you get a copy of the slice's pointer, which is the same.
ArrayList is like that. You can click through in the std documents to see exactly what fields these types have.