> ## Content Index
> Fetch the complete content index at: https://nolongerset.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# The "Unset" Enum Item
- URL: https://nolongerset.com/unset-enum-item/
- Published: 2021-07-21T21:34:26.000Z
- Updated: 2026-05-08T13:03:31.000Z
- Description: This simple technique is a foolproof way to avoid the sort of logic bug that can live undetected in your codebase for years.
- Author: Mike Wolfe
- Tags: Intermediate, Defensive Programming, #Import 2026-05-20 02:59

A good bit of [defensive programming](https://nolongerset.com/defensive-programming/) is to include an "Unset" item within your enum declarations.

## Unintentional Default Values - UNSAFE

Let's say you have an invoicing program that applies discounts and penalties to amounts owed.

- If paid early, the customer gets a 2% discount
- If paid on-time, the customer pays full price
- If paid late, the customer pays a 10% penalty

You might represent these three possibilities with an enumerated type. You could then write a function that took the full price the customer owed and the payment period in which they paid and produced an appropriate amount due.

```vba
Enum pp__PaymentPeriod_UNSAFE
    pp_Discount
    pp_Face
    pp_Penalty
End Enum

Private Function CalcAmtOwed_UNSAFE(BaseAmt As Currency, _
                    PaymentPeriod As pp__PaymentPeriod)
    
    Select Case PaymentPeriod
    Case pp_Discount
        CalcAmtOwed_UNSAFE = Round(BaseAmt * 0.98, 2)
    Case pp_Face
        CalcAmtOwed_UNSAFE = BaseAmt
    Case pp_Penalty
        CalcAmtOwed_UNSAFE = Round(BaseAmt * 1.1, 2)
    Case Else
        Throw "Invalid PaymentPeriod: {0}", PaymentPeriod
    End Select
    
End Function
```

This code is **UNSAFE** because if we fail to set the *PmtPeriod* variable, it defaults to *pp\_Discount* (which is probably not what we want).

But what happens if we forget to initialize the *PaymentPeriod* value–or if the code execution resets? In VBA, enums are little more than glorified Long integers. That means that the default value of a typical enum is the first item in the list. In this case, that would be *pp\_Discount*.

Here's what could happen:

```vba
Sub UnsafeCalc()
    Dim PmtPeriod As pp__PaymentPeriod
    
    Debug.Print CalcAmtOwed_UNSAFE(100, PmtPeriod)
End Sub
```

When we run this *UnsafeCalc* procedure, which uses the above unsafe code, the value printed to the immediate window is 98\. 

![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2021/07/image-22.png)

In other words, the *CalcAmtOwed* function applied a 2% discount even though we **never explicitly intended to do so**. This is an example of a logic bug. Logic bugs are the [worst kind of all](https://nolongerset.com/some-bugs-are-better-than-others/), because they can go undetected for so long. 

I can easily imagine an application where every few months some obscure edge case results in the *PaymentPeriod* variable being reinitialized and a customer receiving an unwarranted 2% discount.

## An Unset Default Item: The SAFER Approach

In the code below, I've introduced an additional item in the *pp\_\_PaymentPeriod* enum: `pp_Unset`. The sole purpose of this item is to act as the placeholder for an uninitialized variable of this type.

Now, when we calculate the amount owed, we can [throw an error](https://nolongerset.com/throwing-errors-in-vba/) if the *PaymentPeriod* variable has not yet been initialized. It's still a runtime error–which we'd like to avoid–but runtime errors are better than logic errors.

```vba
Private Enum pp__PaymentPeriod
    pp_Unset
    pp_Discount
    pp_Face
    pp_Penalty
End Enum

Private Function CalcAmtOwed(BaseAmt As Currency, _
                             PaymentPeriod As pp__PaymentPeriod)
    Select Case PaymentPeriod
    Case pp_Unset
        Throw "PaymentPeriod value never set"
    Case pp_Discount
        CalcAmtOwed = Round(BaseAmt * 0.98, 2)
    Case pp_Face
        CalcAmtOwed = BaseAmt
    Case pp_Penalty
        CalcAmtOwed = Round(BaseAmt * 1.1, 2)
    Case Else
        Throw "Invalid PaymentPeriod: {0}", PaymentPeriod
    End Select
    
End Function

Sub SafeCalc()
    Dim PmtPeriod As pp__PaymentPeriod
    
    Debug.Print CalcAmtOwed(100, PmtPeriod)
End Sub
```

A **SAFER** way to work with enums.

If we run the `SafeCalc` routine in an immediate window, we get the following runtime error:

![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2021/07/image-21.png)

This is good, because the only thing worse than a bug in your software is a bug in your software that you never discover.

### Alternative approach

Another option would be to override the default start value of the enum by assigning a positive number to the first item. This achieves our objective of preventing an uninitialized `pp__PaymentPeriod` variable being assigned a default value of `pp_Discount`.

```vba
Enum pp__PaymentPeriod
    pp_Discount = 1    'Override the default start value of 0
    pp_Face            'VBA implicitly assigns pp_Face a value of 2
    pp_Penalty         'VBA implicitly assigns pp_Penalty a value of 3
End Enum
```

I don't like this as much because it's less explicit than having a *pp\_Unset* item to hold the value of 0\. It's potentially misleading, too. As a code reader, I would assume that the number 1 had some special meaning with regards to *pp\_Discount*. In fact, there's nothing special about the number 1 in this code other than *it is not zero*. I much prefer the explicit *pp\_Unset* enum item approach.

### Side note

If you're curious about my unorthodox enum naming convention, I wrote an [article](https://nolongerset.com/enum-type-naming-convention/) about it.

---

### Referenced articles

[Throwing Errors in VBAIntroducing a frictionless alternative to Err.Raise.![](https://nolongerset.com/favicon.png)No Longer SetMike Wolfe![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2020/10/juggler-2329843_1920.jpg)](https://nolongerset.com/throwing-errors-in-vba/)

[Defensive ProgrammingDon’t build digital Maginot Lines. Program your defenses in depth.![](https://nolongerset.com/favicon.png)No Longer SetMike Wolfe![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2020/10/Maginot_line_1_downsized.jpg)](https://nolongerset.com/defensive-programming/)

[Some Bugs are Better than OthersNot all bugs are created equal. Avoid the expensive ones by making more of the ones that are easy to find and fix.![](https://nolongerset.com/favicon.png)No Longer SetMike Wolfe![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2021/03/car-2122580_1920.jpg)](https://nolongerset.com/some-bugs-are-better-than-others/)

[Enum Type Naming ConventionThe combination of “IntelliSense overload” and “global identifier case changes” convinced me I needed a different approach.![](https://nolongerset.com/favicon.png)No Longer SetMike Wolfe![](https://storage.ghost.io/c/eb/d7/ebd732c1-5f03-4f07-b386-5d08557e15c9/content/images/2020/10/scrabble-15546_1920.jpg)](https://nolongerset.com/enum-type-naming-convention/)

*Image by [Capri23auto](https://pixabay.com/users/capri23auto-1767157/?utm%5Fsource=link-attribution&utm%5Fmedium=referral&utm%5Fcampaign=image&utm%5Fcontent=4248324) from [Pixabay](https://pixabay.com/?utm%5Fsource=link-attribution&utm%5Fmedium=referral&utm%5Fcampaign=image&utm%5Fcontent=4248324)*

***UPDATE \[2021-07-22\]*** *: Updated the first code sample to remove the `= 1` from the top enum item (h/t [](https://twitter.com/philivc/)[Philipp Stiefel](https://twitter.com/philivc/status/1418079948112384000)).*