Maybe Treasure, Maybe Nothing: returning and unwrapping option types
In this lesson you'll learn to
- βStudent can return ?t from a function and safely unwrap it with if-then binding and the ? operator.
Student can return ?t from a function and safely unwrap it with if-then binding and the ? operator.
π Reference & full walkthrough: Maybe Treasure, Maybe Nothing: returning and unwrapping option types
π Builds on: north-jungle: verse-optionals fundamentals
π§© Your capstone piece: FindWinner()?player β the safe race-winner lookup (companions: verse-optionals PASS, sug-handling-optional-types PASS, sug-safe-optional-value-access PASS)
What you'll learn
- What an
option(?type) value is and why functions return one - How to build
option{X}on success andfalseon failure - How to unwrap an option safely with
if (X := Maybe?): - How to react with real devices: read a button, show a HUD widget, and fire a trigger
How it works
An option holds either one value (option{X}) or nothing (false). You write its type with a leading ?, so ?int is "maybe an int". A function that returns an option is the honest way to answer a question that might have no answer.
The key rule: you can only unwrap an option inside a failure context. The idiom is if (Value := Maybe?): β the query operator ? succeeds and binds Value only when the option holds something; the else: branch runs when it's empty. There is no nil and no == nil check in Verse.
In our device, FindBuoyIndex searches a small array of "valid" buoy numbers and returns ?int β the index if found, or false if not. When a pirate interacts with the button we call it, then:
- If we get a real index, we build a HUD message and show it with a real
text_blockwidget viaGetPlayerUI[]/AddWidget, thenTriggerthe cannon. - If not, we show a 'no match' widget and do nothing else.
Notice GetPlayerUI[...] uses [] because it's a fallible lookup, and array indexing ValidBuoys[i] is also fallible β both must live inside an if/for.
Let's build it
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# A cove device that returns an option (?int) and unwraps it to drive real devices + HUD.
option_cove_device := class(creative_device):
@editable
BuoyButton : button_device = button_device{} # pirate interacts here
@editable
CannonTrigger : trigger_device = trigger_device{} # fires only on a real match
# The buoy numbers we consider "ours". Search returns an option over these.
ValidBuoys : []int = array{3, 7, 9}
# RETURNS AN OPTION: option{index} if Target is a valid buoy, else false.
FindBuoyIndex(Target : int) : ?int =
for (I := 0..ValidBuoys.Length - 1):
if (Buoy := ValidBuoys[I], Buoy = Target):
return option{I}
false # nothing matched -> empty option
OnBegin<override>()<suspends> : void =
# React whenever a pirate presses the buoy button.
BuoyButton.InteractedWithEvent.Subscribe(OnBuoyPressed)
OnBuoyPressed(Agent : agent) : void =
# Pretend the pressed buoy is number 7 (a valid one) for this demo.
Maybe := FindBuoyIndex(7)
# UNWRAP the option in a failure context:
if (Index := Maybe?):
ShowHUD(Agent, "Match! Buoy at slot {Index} β firing cannon.")
CannonTrigger.Trigger(Agent) # only fires when there is a real value
else:
ShowHUD(Agent, "No matching buoy for this code.")
# Show a text widget on the interacting player's HUD (real player-UI calls).
ShowHUD(Agent : agent, Text : string) : void =
if (Player := player[Agent], UI := GetPlayerUI[Player]):
Label := text_block{DefaultText := StringToMessage(Text)}
UI.AddWidget(Label)
# Wrap a string as a message so a text_block can display it.
StringToMessage<localizes>(Value : string) : message = "{Value}"
Try it yourself
- Place a Button device and a Trigger device in your cove and assign them to
BuoyButtonandCannonTrigger. - Build Verse, playtest, and press the button β you should see "Match! Buoy at slot 1" and the trigger fires.
- Change
FindBuoyIndex(7)toFindBuoyIndex(4)(not inValidBuoys). Now you get the "No matching buoy" HUD and the cannon stays silent. - Add a fourth number to
ValidBuoysand search for it to confirm theoption{I}path still works.
Recap
- An
option(?type) holds one value (option{X}) or nothing (false). - Functions that might have no answer should return an option instead of crashing or guessing.
- Unwrap with
if (X := Maybe?):β the body runs only when a value is present;else:handles empty. - Fallible lookups like
GetPlayerUI[],player[Agent], andArr[i]must live inside a failure context. - We used the option to gate real behavior: show a HUD widget and fire a trigger only on a genuine match.
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 lessonSources
/guides/return-an-optionBiloxi Studios original lesson
Add this lesson to your free study plan.
π§ The Keeperβs log
Quest complete? Chart your next heading from the π¦ West Coves expedition.
β΅ Take me there