Verse Wiki — the Verse handbook for Blueprint authors
Bonus · EXTRA

Building Stacks and Queues with Immutable Arrays

No push, no pop, and the array itself can't be changed — and you can still build stacks and queues? You can, and the result is downright elegant. This page assembles Lesson 16's four-piece kit (.Length, grabs that can come up empty, Slice, +=) into two classic structures, and gives you a feel for "arrays are values — swap the whole row" in real combat.

1. A Change of Mindset: Don't Edit the Container — Replace It

A stack is last-in, first-out, like stacking plates: new plates go on top, and you take from the top too. A queue is first-in, first-out, like lining up for loadout: enter at the tail, leave from the head. Elsewhere these are built with in-place moves like push / pop / dequeue; Verse arrays are values without a single in-place move available — no matter. Flip the mindset and it clicks: every operation builds a new row and replaces the entire old row in the variable.

Set up the mapping first: push / enqueue = tail concatenation set Xs += array{X}; pop = read the last item Xs[Xs.Length - 1], then cut off the tail with Slice[0, Length - 1]; dequeue = read the first item Xs[0], then keep the rest with Slice[1, Length]. Epic's official docs devote a page to this game plan, "Stacks and Queues in Verse"; the implementations below are the standard treatment of the topic — for fine details, defer to the official page.

2. The Stack: An Undo System

The most classic stack in games is undo: every action gets pushed onto the stack; the undo key pops the most recent one. Look at the Pop function — it grabs a slot and then calls Slice, and both steps might not succeed, so the whole function's nameplate carries <transacts><decides> and it gets called with square brackets: Pop[]. When the stack is empty, Stack[Stack.Length - 1] is Stack[-1], guaranteed to come up empty, and the whole Pop fails to go through with it — the rule "can't pop an empty stack" grew straight out of the machinery without you writing a single defensive check.

undo_stack_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

undo_stack_device := class(creative_device):

    # Stack: last in, first out; the tail is the top
    var Stack:[]string = array{}

    Push(Item:string):void =
        set Stack += array{Item}

    Pop()<transacts><decides>:string =
        Top := Stack[Stack.Length - 1]
        set Stack = Stack.Slice[0, Stack.Length - 1]
        Top

    OnBegin<override>()<suspends>:void =
        Push("Break Wall")
        Push("Build Ramp")
        Push("Place Door")
        if (Undone := Pop[]):
            Print("Undid: {Undone}")
        Print("{Stack.Length} steps left on the stack")
        if (A := Pop[], B := Pop[]):
            Print("Undid two more: {A} and {B}")
        if (Empty := Pop[]):
            Print("Never gets here")
        else:
            Print("Stack is empty, Pop failed — no crash, just a failure")

Run the mental simulation: after three Pushes the stack holds Break Wall, Build Ramp, Place Door; the first Pop[] ejects the finale, Place Door; then A and B pop Build Ramp and Break Wall in turn; the last Pop[] faces an empty stack, crisply fails to go through, and swerves into else. One hidden bonus: in if (A := Pop[], B := Pop[]), if the B step doesn't go through, the item A already popped gets undone wholesale — stuffed back onto the stack. A "half-popped" middle state simply cannot occur (see the deep-dive page for the why).

3. The Queue: A Matchmaking Waiting Room

The queue flips direction: entry stays at the tail, but exit moves to the head of the line. The classic scenario is matchmaking — first player in line, first into the match:

match_queue_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

match_queue_device := class(creative_device):

    # Queue: first in, first out; the head is slot 0
    var Queue:[]string = array{}

    Enqueue(Item:string):void =
        set Queue += array{Item}

    Dequeue()<transacts><decides>:string =
        First := Queue[0]
        set Queue = Queue.Slice[1, Queue.Length]
        First

    OnBegin<override>()<suspends>:void =
        Enqueue("Bubbles")
        Enqueue("Dopey")
        if (Next := Dequeue[]):
            Print("{Next} enters the match")
        Print("Still in line: {Queue.Length}")

Dequeue's two steps: Queue[0] reads the head of the line, Slice[1, Queue.Length] keeps everyone from slot 1 to the end — precisely "everyone but the head". On an empty queue, Queue[0] comes up empty and the whole Dequeue fails to go through — not one defensive check needed, same as the stack. There is a price, admittedly: every dequeue builds the row anew from scratch, which isn't the leanest approach when the line gets very long — but at the scale of island logic, clarity beats everything.

4. Pop Quiz

The stack is empty and you call Pop[]. What happens?

After Dequeue runs, what happened to the original Queue array itself?

Sources & Further Reading

The topic of this page comes from Epic's official documentation, "Stacks and Queues in Verse" (dev.epicgames.com ↗); the implementations shown are the standard treatment of the topic — for exact details, defer to the official page. To dig further into the "failure as a business rule" style, head to this lesson's deep-dive, "Why Out-of-Bounds Doesn't Throw".