unique: Object Identity and the Secret of comparable
In many languages, checking whether two objects are the same one is a quick comparison away. Verse refuses: instances built from an ordinary blueprint class have no "identity" at all — you're not even allowed to compare them with =. This article unpacks how <unique> issues objects an "ID card", and what earns player the right to go into a Map variable as a key.
1. The Default: Objects Carry No ID Card
Start with a thought experiment: A := monster{Name := "Slime"}, then an identical B := monster{Name := "Slime"} — are A and B "equal"? Compared by content, their fields match perfectly; compared by identity, they're two different monsters. Many languages default to comparing "is it the same object"; Verse goes harder: ordinary class instances get neither comparison. Write if (A = B): and Compile goes straight to red, saying the type is "not comparable" — not that the comparison comes out false; the line can't even be written.
This is on purpose: at heart, Verse is more of a "content is all that matters" language — a value (like 42 or "hello") has only content, no "this one" versus "that one". By default, instances built from a blueprint class are treated as that same content-only kind of thing. Giving one an "identity" takes an explicit application — and the application form is hanging the <unique> angle-bracket tag on the class.
2. <unique>: An ID Card Issued at Construction
With <unique> attached, the engine assigns every constructed instance a one-of-a-kind identity. From then on, instances can be compared with = and <> — an identity comparison: not "do the fields match", but "is it the same one":
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
# unique: every instance receives a unique identity at construction
session_token := class<unique>:
var Score:int = 0
token_device := class(creative_device):
OnBegin<override>()<suspends>:void =
A := session_token{}
B := session_token{}
C := A
# = is a comparison in a failure context: it succeeds when identities match
if (A = C):
Print("A and C are the same instance")
if (A = B):
Print("This line never prints: A and B match in content, differ in identity")
Translating the drawing: session_token carries <unique>, so every time you write {} to build one, the engine issues a fresh ID card. A and B are built separately — two different cards; C := A builds nothing new, it just sticks a second name on A's card. The two ifs at the end act as two Branch nodes: the A = C wire goes through (same card) and its line prints; A = B doesn't (two different cards), so execution leaves through the False pin and that line never prints.
Look at those two closing ifs: A = C succeeds because C := A merely gave the same instance a second name (Lesson 20: blueprint objects are "references" — giving one a new name doesn't copy it); A = B fails — the two tokens' fields are identical, but they're two instances constructed one after the other, holding different ID numbers. The comparison is simply a Branch: pass and you continue down the wire; fail and the whole wire is skipped — no crash, no exception popup.
3. The Ticket to Being a map Key: comparable
<unique> comes with a hidden perk: a unique class automatically counts as comparable. And "comparable" is precisely the ticket to being a map key — a map must be able to decide "does this key already exist", which demands equality comparison on keys. Hence:
# Instances of a unique class are comparable, so they can be map keys
var Registry:[session_token]string = map{}
Translating the drawing: this single line creates a Map variable Registry whose key type is session_token (possible precisely because it's unique, hence comparable) and whose value type is String — the same as creating a Map variable in a Blueprint and picking its key and value types.
This also solves a mystery you may have been carrying for a while: why can player be a map key? Take the classic var Scores:[player]int = map{} — it works because the engine designed types like player in exactly this "unique identity, comparable" style: every player is inherently "the one and only", so keying by identity is the most natural thing in the world. Every player scoreboard you've ever written in a game stands on this unique + comparable machinery. The official Comparable in Verse page lists the complete roster of comparable types — old friends like int and string, compared by content, are on it too.
To close, savor the philosophy behind the design: identity means "two things with identical content can still be told apart", which is really a "stateful" kind of thing, never quite at home with Verse's content-only foundation. Epic made identity opt-in (absent by default; say so if you want it), preserving the language's pure core while leaving a realist's back door for game objects (players, items, sessions). When should you add <unique>? Ask yourself one question: "In my gameplay, do two instances with completely identical fields count as two different things?" If yes — add it.
4. Pop Quiz ◇
A and B are two instances of an ordinary class (no <unique>). What happens when you write if (A = B):?
After adding <unique> to a class, which of these becomes possible?
Sources & Further Reading
This article draws on the official Epic documentation:
▸ Official docs: unique specifier ↗