Mastering Optional Types in Verse with the ? Operator
Tutorial beginner compiles

Mastering Optional Types in Verse with the ? Operator

Updated beginner Code verified

What you'll learn

You will learn how to declare optional values (?t), wrap values into options with option{}, and safely unwrap them with the ? operator inside a failure context. This lets you handle "no value" situations explicitly instead of crashing or guessing.

How it works

An option type (?t) either holds a value of type t or is empty. The empty option is spelled false — there is no nil in Verse.

There are two halves of the bridge:

  • option{Expr} wraps a value (or a <decides> expression) into an option. If Expr fails, the option becomes false.
  • Maybe? unwraps the option into a <decides> expression. Because this can fail, ? may only appear in a failure context — typically if (V := Maybe?):. When the option holds a value, V is bound to it; when empty, the if fails and control goes to else:.

A subtle trap: do not wrap a value that is already non-optional and call ? on it expecting magic. The ? operator works on options only. Likewise, never test a bare option — always bind it (if (V := Opt?):).

Let's build it

This device reads each player, builds an optional message, and unwraps it safely. We seed an empty option (false), then assign it with set ... = option{}, proving both states of the lifecycle. Trigger events drive the demonstration so you can re-run it live.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

# Demonstrates declaring, wrapping, and safely unwrapping option (?t) values.
option_demo_device := class<concrete>(creative_device):

    # Wire this to a Trigger Device in your level to re-run the demo.
    @editable Trigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>: void =
        # Run once at start...
        RunDemo()
        # ...and again each time the trigger fires.
        Trigger.TriggeredEvent.Subscribe(OnTriggered)

    # Trigger handler — payload is an optional agent (?agent), itself an option!
    OnTriggered(MaybeAgent : ?agent): void =
        # Unwrap the trigger's optional agent before using it.
        if (Agent := MaybeAgent?):
            Print("Trigger fired by an agent — re-running demo.")
        else:
            Print("Trigger fired with no agent.")
        RunDemo()

    RunDemo(): void =
        # 1. An empty option: 'false' means "no value yet".
        var MaybeName : ?string = false

        # Unwrapping an empty option fails, so this 'else' runs.
        if (Name := MaybeName?):
            Print("Name is: {Name}")
        else:
            Print("No name set yet (option is false).")

        # 2. Wrap a concrete value into the option with option{}.
        set MaybeName = option{"Felicity"}

        # 3. Now the unwrap succeeds and binds 'Name'.
        if (Name := MaybeName?):
            Print("Unwrapped name: {Name}")
        else:
            Print("Still empty.")

        # 4. option{} can wrap a <decides> expression: success -> value, failure -> false.
        Players := GetPlayspace().GetPlayers()
        FirstPlayer : ?player = option{Players[0]}  # fails to false if list is empty
        if (P := FirstPlayer?):
            Print("First player found in the playspace.")
        else:
            Print("No players are present.")

Try it yourself

  1. Place the device in your level.
  2. Add a Trigger Device and wire it to the Trigger editable field.
  3. Playtest and watch the console — you'll see the empty-then-filled lifecycle print at start.
  4. Step on / activate the trigger to re-run the demo and see the optional agent payload unwrap.
  5. Challenge: change step 2 to set MaybeName = option{SomeFailingCheck[]} and observe how a failed <decides> call yields false.

Recap

  • Use ?t to declare an optional type; the empty option is false (never nil).
  • Use option{Value} to wrap a value — or wrap a <decides> call so failure becomes false.
  • Use if (V := Opt?): to safely unwrap and bind; handle the empty case in else:.
  • ? is fallible, so it only works inside a failure context — never test a bare option.

Check your understanding

Test yourself with an interactive quiz and track your progress + earn XP — free for members.

Turn this into a guided course

Add Handling Optional Types with the ? Operator in Verse to your free study plan — we'll suggest related pages and stitch the lot into one compile-checked, self-guided lesson with worked examples and quizzes.

Original tutorial generated by Verse Island from the Verse/UEFN knowledge base, with references to the Epic Games sources above. Code is validated against the knowledge base.

Comments

    Sign in to vote, comment, or suggest an edit. Sign in