# Why does Deque.at return T instead of ?T

**URL:** <https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146>\
**Category:** Explain\
**Created:** [August 9, 2026, 3:56am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146 "2026-08-09T03:56:36Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![jlarator](https://ziggit.dev/letter_avatar_proxy/v4/letter/j/f17d59/32.png) [@jlarator](https://ziggit.dev/u/jlarator)\
**Post date:** [August 9, 2026, 3:56am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/1 "2026-08-09T03:56:36Z")

</div>

I understand that the implementation for zig 0.16.0 is

```zig
pub fn at(deque: *const Self, index: usize) T {
    assert(index < deque.len);
    return deque.buffer[deque.bufferIndex(index)];
}

```

I would have expected to be something like this.

```zig
pub fn at(deque: *const Self, index: usize) ?T {
    if(index >= deque.len) return null
    return deque.buffer[deque.bufferIndex(index)];
}

```

Is there a reason why the function does not return an optional? the optional would be better because it gives you the static check instead of a runtime crash.

Since this is my first question about the source code, i thought id ask first before i suggest changes.

---

<div class="post-metadata">

**Author:** ![tholmes](https://ziggit.dev/user_avatar/ziggit.dev/tholmes/32/5641_2.png) [@tholmes](https://ziggit.dev/u/tholmes)\
**Post date:** [August 9, 2026, 4:00am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/2 "2026-08-09T04:00:02Z")

</div>

My guess is that since the caller already has access to the deque’s length, they figure it’s up to the caller to bounds-check the index and simply assert that the caller did.  
This enables cases where, for better performance, when the caller knows for certain that they don’t need to bounds-check and the index will be in bounds, the function can just blindly trust the index the caller provided.

---

<div class="post-metadata">

**Author:** ![jlarator](https://ziggit.dev/letter_avatar_proxy/v4/letter/j/f17d59/32.png) [@jlarator](https://ziggit.dev/u/jlarator)\
**Post date:** [August 9, 2026, 4:04am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/3 "2026-08-09T04:04:39Z")

</div>

i’d agree, except that the caller would also have access to the buffer directly and would not need to access items via access functions. I wonder if it’s just a temporary implementation on purpose

---

<div class="post-metadata">

**Author:** ![g41797](https://ziggit.dev/user_avatar/ziggit.dev/g41797/32/3339_2.png) [@g41797](https://ziggit.dev/u/g41797)\
**Post date:** [August 9, 2026, 5:22am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/4 "2026-08-09T05:22:59Z")

</div>

Looks like it was done for performance improvement

Assert below is kind of guard

```zig
assert(index < deque.len);

```

btw i also think that ?T is better…

---

<div class="post-metadata">

**Author:** ![WeeBull](https://ziggit.dev/user_avatar/ziggit.dev/weebull/32/6828_2.png) [@WeeBull](https://ziggit.dev/u/WeeBull)\
**Post date:** [August 9, 2026, 5:18pm UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/5 "2026-08-09T17:18:37Z")

</div>

If you look at the [docs for that function](https://ziglang.org/documentation/0.16.0/std/#std.deque.Deque.at) you’ll see it says

> Asserts that the index is in-bounds.

By which it means, it **assumes** the index is in bounds.

This is quite common for a lot of the standard library. Asserts enforcing the working assumptions of code, giving you safety checks in safe builds and removing them for fast builds. It’s up to the caller to ensure that what is asserted is true. `pushBack()` and `pushBackAssumeCapacity()` is a good example on `std.deque`. The first “ensures” there’s at least one unused space, and the calls `pushBackAssumeCapacity()` which asserts that there is capacity before doing the push.

Generally, it means you can make the check once and then do multiple things to the data-structure without repeating the check every call.

---

<div class="post-metadata">

**Author:** ![pancelor](https://ziggit.dev/user_avatar/ziggit.dev/pancelor/32/9684_2.png) [@pancelor](https://ziggit.dev/u/pancelor)\
**Post date:** [August 12, 2026, 7:17am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/6 "2026-08-12T07:17:41Z")

</div>

There’s no performance difference between

```zig
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

```zig
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:

```zig
/// 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](https://github.com/ziglang/zig/pull/19424), particularly this [comment](https://github.com/ziglang/zig/pull/19406#discussion_r1536731443).

I don’t know why `Deque.at` didn’t get the same treatment, maybe it was overlooked? @nektro do you have thoughts on this?

---

<div class="post-metadata">

**Author:** ![dpirch](https://ziggit.dev/user_avatar/ziggit.dev/dpirch/32/5321_2.png) [@dpirch](https://ziggit.dev/u/dpirch)\
**Post date:** [August 12, 2026, 8:46am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/7 "2026-08-12T08:46:58Z")

</div>

for `pop` it makes sense to return an optional because it is meant to be used when you don’t know whether the list is empty. Deque also has `popFront`/`popBack` as well as `front`/`back` functions which all return optionals.

For `at` this does not really make sense, because to have a meaningful index, you usually already know the size of the list.

---

<div class="post-metadata">

**Author:** ![pancelor](https://ziggit.dev/user_avatar/ziggit.dev/pancelor/32/9684_2.png) [@pancelor](https://ziggit.dev/u/pancelor)\
**Post date:** [August 12, 2026, 9:41pm UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/8 "2026-08-12T21:41:29Z")

</div>

Ah, good point. I suppose `mydeque.at(7)` is acting similar to the interface for normal arrays: `myarray[7]`

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [August 13, 2026, 2:07am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/9 "2026-08-13T02:07:51Z")

</div>

> [@WeeBull](#):
>
> By which it means, it **assumes** the index is in bounds.

The word “asserts” here is following [doc comment guidance](https://ziglang.org/documentation/0.16.0/#Doc-Comment-Guidance).

---

<div class="post-metadata">

**Author:** ![jlarator](https://ziggit.dev/letter_avatar_proxy/v4/letter/j/f17d59/32.png) [@jlarator](https://ziggit.dev/u/jlarator)\
**Post date:** [August 13, 2026, 3:32am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/10 "2026-08-13T03:32:48Z")

</div>

would you guys agree that it would better to shift the validity access check to the language API via ?T, rather than using the assertion.

I understand that they have the same performance, but i think the optional is more beneficial because it provides with static the static check.

---

<div class="post-metadata">

**Author:** ![andrewrk](https://ziggit.dev/user_avatar/ziggit.dev/andrewrk/32/7011_2.png) [@andrewrk](https://ziggit.dev/u/andrewrk)\
**Post date:** [August 13, 2026, 3:39am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/11 "2026-08-13T03:39:55Z")

</div>

No, for the same reason that array index syntax (`[i]`) does not return an optional. The word “at” when used in an arraylike data structure here means that it is performing an operation equivalent to array indexing.

---

<div class="post-metadata">

**Author:** ![jlarator](https://ziggit.dev/letter_avatar_proxy/v4/letter/j/f17d59/32.png) [@jlarator](https://ziggit.dev/u/jlarator)\
**Post date:** [August 13, 2026, 3:52am UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/12 "2026-08-13T03:52:46Z")

</div>

I think i understand the point you are making.

I still think that it would be beneficial to have a function that does return an optional like @pancelor suggests, am not sure what a good name for this function would be. maybe “get”?

---

<div class="post-metadata">

**Author:** ![WeeBull](https://ziggit.dev/user_avatar/ziggit.dev/weebull/32/6828_2.png) [@WeeBull](https://ziggit.dev/u/WeeBull)\
**Post date:** [August 13, 2026, 4:20pm UTC](https://ziggit.dev/t/why-does-deque-at-return-t-instead-of-t/17146/13 "2026-08-13T16:20:30Z")

</div>

I think the wording is fine as a standardised comment. I just don’t think it reads very naturally. That’s why I rephrased it.

In conversational language, if somebody “assets” something, they are making a statement that they believe to be true. I assert “the sun is hot”. It’s something I know is true.

These functions don’t know the index is in range. That’s why they safety-check it. Maybe I should have said they “trust but verify” the index is in range.
