Case statement

Example:

  1. let line = readline(stdin)
  2. case line
  3. of "delete-everything", "restart-computer":
  4. echo "permission denied"
  5. of "go-for-a-walk": echo "please yourself"
  6. elif line.len == 0: echo "empty" # optional, must come after `of` branches
  7. else: echo "unknown command" # ditto
  8. # indentation of the branches is also allowed; and so is an optional colon
  9. # after the selecting expression:
  10. case readline(stdin):
  11. of "delete-everything", "restart-computer":
  12. echo "permission denied"
  13. of "go-for-a-walk": echo "please yourself"
  14. else: echo "unknown command"

The case statement is similar to the if statement, but it represents a multi-branch selection. The expression after the keyword case is evaluated and if its value is in a slicelist the corresponding statements (after the of keyword) are executed. If the value is not in any given slicelist, trailing elif and else parts are executed using same semantics as for if statement, and elif is handled just like else: if. If there are no else or elif parts and not all possible values that expr can hold occur in a slicelist, a static error occurs. This holds only for expressions of ordinal types. “All possible values” of expr are determined by expr’s type. To suppress the static error an else: discard should be used.

Only ordinal types, floats, strings and cstrings are allowed as values in case statements.

For non-ordinal types, it is not possible to list every possible value and so these always require an else part. An exception to this rule is for the string type, which currently doesn’t require a trailing else or elif branch; it’s unspecified whether this will keep working in future versions.

Because case statements are checked for exhaustiveness during semantic analysis, the value in every of branch must be a constant expression. This restriction also allows the compiler to generate more performant code.

As a special semantic extension, an expression in an of branch of a case statement may evaluate to a set or array constructor; the set or array is then expanded into a list of its elements:

  1. const
  2. SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
  3. proc classify(s: string) =
  4. case s[0]
  5. of SymChars, '_': echo "an identifier"
  6. of '0'..'9': echo "a number"
  7. else: echo "other"
  8. # is equivalent to:
  9. proc classify(s: string) =
  10. case s[0]
  11. of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_': echo "an identifier"
  12. of '0'..'9': echo "a number"
  13. else: echo "other"

The case statement doesn’t produce an l-value, so the following example won’t work:

  1. type
  2. Foo = ref object
  3. x: seq[string]
  4. proc get_x(x: Foo): var seq[string] =
  5. # doesn't work
  6. case true
  7. of true:
  8. x.x
  9. else:
  10. x.x
  11. var foo = Foo(x: @[])
  12. foo.get_x().add("asd")

This can be fixed by explicitly using result or return:

  1. proc get_x(x: Foo): var seq[string] =
  2. case true
  3. of true:
  4. result = x.x
  5. else:
  6. result = x.x