There’s no performance difference between
const Deque1 = struct {
...
pub fn at(deque: *const Self, index: usize) u32 {
assert(index < deque.len);
return deque.buffer[deque.bufferIndex(index)];
}
};
var d1 = Deque1{};
...
const v1: u32 = d1.at(7);
and
const Deque2 = struct {
...
pub fn at(deque: *const Self, index: usize) ?u32 {
if (index >= deque.len) return null;
return deque.buffer[deque.bufferIndex(index)];
}
};
var d2 = Deque2{};
...
const v2: u32 = d2.at(7) orelse unreachable;
When compiled in release mode, the optimizer will treat these both the same way.
the optional would be better because it gives you the static check instead of a runtime crash
I agree, the second/optional version is more flexible (you can orelse unreachable or .? if you want the original behavior) and harder to mis-use.
(edit: I’ve changed my mind after further discussion here, I like it better how it is now)
std.ArrayList used to have this, before 0.14.0:
/// Remove and return the last element from the list.
/// Asserts the list has at least one item.
/// Invalidates pointers to the removed element.
pub fn pop(self: *Self) T {
const val = self.items[self.items.len - 1];
self.items.len -= 1;
return val;
}
/// Remove and return the last element from the list, or
/// return `null` if list is empty.
/// Invalidates pointers to the removed element, if any.
pub fn popOrNull(self: *Self) ?T {
if (self.items.len == 0) return null;
return self.pop();
}
They were merged into a single fn pop(self: *Self) ?T, see discussion + links here: std.{ArrayList,ArrayHashMap,MultiArrayList,BoundedArray}: popOrNull() -> pop() by nektro · Pull Request #19424 · ziglang/zig · GitHub, particularly this comment.
I don’t know why Deque.at didn’t get the same treatment, maybe it was overlooked? @nektro do you have thoughts on this?