Optionals: Maybe There Is a Vehicle

In this lesson you'll learn to

  • βœ“Declare ?type, unwrap with if(X := MaybeY?), and return false-y absence safely.

Declare ?type, unwrap with if(X := MaybeY?), and return false-y absence safely.

πŸ“– Reference & full walkthrough: Optionals: Maybe There Is a Vehicle

🧩 Your capstone piece: Safe agentβ†’fort_character and vehicle lookups


Optionals: Verse's Honest "Maybe"

Half the crashes in most programming languages come from one mistake: using something that turned out not to be there. A player who left mid-match. A chest that was already looted. A character that hasn't spawned yet. Verse refuses to let you make that mistake silently. Its answer is the option type β€” a value that is either present or absent, and a language that won't let you touch the inside until you've proven it's present.

This lesson is the foundation for the next two (failure contexts and transacts), because options and failure are two faces of the same idea. Get options right and a whole category of bugs simply stops happening.

What an option actually is

<!-- section-art:what-an-option-actually-is --> Optionals: Verse's Honest "Maybe": What an option actually is

The Optional Split

An option is written with a leading ? on the type. ?int means "maybe an int." ?agent means "maybe an agent." A value of that type holds either one value of the inner type or nothing β€” Verse calls the empty case false.

# A plain int always has a value.
Lives : int = 3

# An option int might have a value, or might be empty.
var BonusLife : ?int = false        # starts empty
set BonusLife = option{1}           # now holds 1

Two pieces of syntax do all the work:

  • false is the empty option. (Yes β€” the same word as the boolean. In option position it means "no value.")
  • option{X} wraps a value X into an option. option{1} is a ?int holding 1.

This is grounded in real shipping code. From our project's gift system, an option field declared empty and later filled:

# Real declaration from daily_gift.verse β€” an option that may have no gift yet.
DailyGift : ?daily_gift = false

And a function that returns an option because the answer might not exist:

# Real signature from a tracking module: a player might have no recorded position.
GetLastPosition(Player : player) : ?vector3 =
    if (Pos := PositionMap[Player]):
        option{Pos}        # found one β€” wrap it
    else:
        false              # nothing recorded β€” return the empty option

Notice the shape: the function's return type is ?vector3, and the two exits are option{Pos} and false. That is the entire vocabulary.

Reading the inside: you must unwrap

You can hold an option freely, but you cannot use the value inside until you prove it's there. There are two ways to unwrap, and choosing the right one is where beginners stumble.

Way 1 β€” the ? postfix operator (only inside a failure context)

Writing MyOption? asks for the value and fails if the option is empty. Because it can fail, you may only write it somewhere failure is allowed β€” most commonly the condition of an if:

# `ActivatedOnStart` is a ?logic (a maybe-boolean). `ActivatedOnStart?`
# asks for its value; the if runs its body only if it succeeds.
if (ActivatedOnStart?):
    StartTheSequence()

This exact pattern β€” if (SomeOption?) β€” appears all over real Verse. It reads as "if this maybe-value is present (and true)."

Way 2 β€” bind-and-test in an if

More often you want the unwrapped value and a guarantee it exists. Bind it in the if condition with :=:

# If BonusLife holds a value, `Extra` is that value (a plain int) inside the body.
if (Extra := BonusLife?):
    set Lives += Extra
    Print("Awarded {Extra} bonus lives")
else:
    Print("No bonus this round")

Inside the then branch, Extra is a real int, not an option β€” the language has proven it's present, so you never deal with the empty case there. Inside else, you handle absence honestly. Every else you write is a crash that never happens.

A worked example: the most common option in the game

The single most-used option in Fortnite Verse is the player's character. agent.GetFortCharacter[] returns a fort_character, but it can fail β€” the player might be downed, leaving, or not yet spawned. The real API signature confirms this:

# From Verse.digest.verse β€” note <decides>: this call can fail.
(InAgent : agent).GetFortCharacter<native><public>()<transacts><decides> : fort_character

Because it's failable, you call it with square brackets [] and bind the result in an if. This is real corpus code:

# Grounded in our codebase β€” get the character, but only act if it's really there.
if (FortChar := Player.GetFortCharacter[]):
    Transform := FortChar.GetTransform()
    Print("Player is at {Transform.Translation}")
else:
    Print("No character right now β€” skip")

GetFortCharacter[] isn't literally an option type β€” it's a failable call (<decides>) β€” but it behaves identically from your side: prove it succeeded, then use the result. The next lesson covers <decides> in full. For now, see the unity: anything that might not be there forces you to check before you use it.

Optional function parameters

A leading ? on a parameter name makes it optional, usually with a default of false. Callers may omit it. This is everywhere in real device code:

# Real signatures β€” the leading ? on the name means the argument is optional.
StartAmbientSounds(?Agent : ?agent = false) : void = ...
SpawnLootType(Object : creative_object_interface, ?SpawnOffset : ?vector3 = false) : void = ...

To pass one, name it with option{}:

StartAmbientSounds(?Agent := option{Player})   # real call from the corpus
StartAmbientSounds()                            # also valid β€” omit it entirely

The pitfalls β€” where options trip people up

<!-- section-art:the-pitfalls-where-options-trip-people-up --> Optionals: Verse's Honest "Maybe": The pitfalls β€” where options trip people up

Unwrapped Option Pitfall

These are the specific mistakes that cost hours. Learn to recognize them.

1. Using the value without unwrapping. This is the headline error.

# WRONG β€” BonusLife is a ?int; you can't add an option to an int.
set Lives += BonusLife

# RIGHT β€” unwrap first.
if (Extra := BonusLife?):
    set Lives += Extra

2. Using ? outside a failure context. MyOption? can fail, so it's illegal where failure isn't allowed (like a plain assignment at the top level of a function). It belongs in an if condition, a for filter, or another failable expression. If the compiler complains that an expression "can fail" where it isn't allowed, you've hit this.

3. Confusing () and []. Failable things β€” option access, <decides> calls, map/array lookups β€” use square brackets []. Ordinary, never-failing calls use parentheses (). GetFortCharacter() is a compile error; GetFortCharacter[] is correct, because the call can fail.

4. Thinking false is only a boolean. In option position, false is the empty option of whatever type is expected. var Best : ?vector3 = false is a perfectly typed empty ?vector3, not a boolean mistake.

5. Forgetting the else. An if that unwraps without an else silently does nothing when the option is empty. Sometimes that's what you want β€” but decide on purpose. Sloppy code skips the empty case; honest code handles it.

6. ?logic is not logic. A ?logic is a maybe-boolean with three states: present-and-true, present-and-false, and absent. if (MyMaybeBool?) succeeds only in the first case. Don't treat a ?logic as if it were a plain logic.

Recap

  • An option (?type) is a value that may be present or absent. Empty is false; wrap with option{X}.
  • You cannot use the inside until you unwrap it β€” that's the whole point.
  • Unwrap with Opt? inside a failure context, or bind-and-test if (X := Opt?) to get the value safely.
  • GetFortCharacter[] and other <decides> calls behave like options: prove success with [] + if, then use the result.
  • Failable access uses []; plain calls use ().
  • Handle the empty case (else) on purpose β€” that's where the crashes you're preventing live.

References

πŸ”’

Keep going β€” free

You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.

Sign in free to continue this lesson
Pass the quiz above to chart this quest in your Journal.

Sources

/guides/verse-optionals
Source

Biloxi Studios original lesson

Guided course

Add this lesson to your free study plan.

🧭 The Keeper’s log

Quest complete? Chart your next heading from the 🐻 North Jungle expedition.

β›΅ Take me there