Should you ever have a `.none` case in an enum?

Take this example:

enum Good {
    case one
    case two
    case three
}

enum Bad {
    case none
    case one
    case two
    case three
}

func foo(good: Good?, bad: Bad?) {
    
    switch good {
    case .one: print("one")
    case .two: print("two")
    case .three: print("one")
    case .none: print("none")
    }

    switch bad { // Error: Switch must be exhaustive
    case .one: print("one")
    case .two: print("two")
    case .three: print("three")
    case .none: print("none") // Assuming you mean 'Optional<Bad>.none'
    case Optional.none: print("none") // Warning: Case is already handled by previous patterns
    }

    switch bad {
    case .one: print("one")
    case .two: print("two")
    case .three: print("three")
    case Bad.none: print("none") // Error
    case Optional.none: print("none")
    }
}

There's an obvious way of fixing this by treating nil separately before the switch, but note that "Good" way of doing switch was totally valid, and it got suddenly broken with the introduction of "none" case.

If you change from none to nope - the problem goes away (but if the app was working before it might be doing something different now, see just below). And equally worrying, the above snippet aside, if you see a case nope in your peer's code, and think "it is a good idea to change nope to none" - the code would probably compile, the warning could be missed (unless warnings are treated as errors which many companies can't afford) and the app starts to behave differently.

enum E {
    case one
    case none // renamed from "nope". what can possibly go wrong.
}

var x: [Int: Bad] = [1 : .one, 2 : .none] // was nope

... some thousands line away in a different file in a project with a few warnings already

let a = x[1] == .one
let b = x[2] == .none // was nope

I wish there was a way to promote that warning into an error (without "treating all warnings errors").

1 Like