Enumerations

5 min read

4 min read

5 min read

6 min read

5 min read

5 min read

4 min read

5 min read

4 min read

An enumeration defines a set of named values, its enumerators:

Slice
module M
{
enum Fruit { Apple, Pear, Orange }
}

This definition introduces a type named Fruit that becomes a new type in its own right. Slice guarantees that the values of enumerators increase from left to right, so Apple compares less than Pear in every language mapping. By default, the first enumerator has a value of zero, with sequentially increasing values for subsequent enumerators.

A Slice enum type introduces a new namespace scope, so the following is legal:

Slice
module M
{
enum Fruit { Apple, Pear, Orange }
enum ComputerBrands { Apple, Dell, HP, Lenovo }
}

The example below shows how to refer to an enumerator from a different scope:

Slice
module M
{
enum Color { Red, Green, Blue }
}
module N
{
struct Pixel
{
M::Color c = Blue;
}
}

Slice does not permit empty enumerations.

Slice also permits you to assign custom values to enumerators:

Slice
const int PearValue = 7;
enum Fruit { Apple = 0, Pear = PearValue, Orange }

Custom values must be unique and non-negative, and may refer to Slice constants of integer types. If no custom value is specified for an enumerator, its value is one greater than the enumerator that immediately precedes it. In the example above, Orange has the value 8.

The maximum value for an enumerator value is the same as the maximum value for int, 2³¹ - 1.

Slice does not require custom enumerator values to be declared in increasing order:

Slice
enum Fruit { Apple = 5, Pear = 3, Orange = 1 } // Legal

Note however that when there is an inconsistency between the declaration order and the numerical order of the enumerators, the behavior of comparison operations may vary between language mappings.

A Slice enumeration maps to the corresponding enum class in C++.

For example:

Slice
enum Fruit { Apple, Pear, Orange }

The generated C++ enumeration is:

C++
enum class Fruit : std::uint8_t { Apple, Pear, Orange };

The underlying type is std::uint8_t when the enumeration's largest enumerator value is not greater than 254, otherwise it's std::int32_t.

Suppose we modify the Slice definition to include a custom enumerator value:

Slice
enum Fruit { Apple, Pear = 3, Orange }

The generated C++ definition now includes an explicit initializer for every enumerator:

C++
enum class Fruit : std::uint8_t { Apple = 0, Pear = 3, Orange = 4 };

The Slice compiler also generates operator<< to “print” the enumerators of the C++ enum. For example:

C++
std::ostream& operator<<(std::ostream& os, Fruit value);

You can suppress the generation of this operator, and tell the Slice compiler you’ll provide your own custom operator<<, with the "cpp:custom-print" metadata. For example:

Slice
["cpp:custom-print"] // we provide our own custom operator<< for this enum
enum Fruit { Apple, Pear = 3, Orange }

A Slice enumeration maps to the corresponding enumeration in C#. For example:

Slice
enum Fruit { Apple, Pear, Orange }

Not surprisingly, the generated C# definition is very similar:

C#
public enum Fruit { Apple, Pear, Orange }

Suppose we modify the Slice definition to include a custom enumerator value:

Slice
enum Fruit { Apple, Pear = 3, Orange }

The generated C# definition now includes an explicit initializer for every enumerator:

C#
public enum Fruit { Apple = 0, Pear = 3, Orange = 4 }

A Slice enumeration maps to the corresponding enumeration in Java. For example:

Slice
enum Fruit { Apple, Pear, Orange }

The Java mapping for Fruit is shown below:

Java
public enum Fruit {
Apple,
Pear,
Orange;
public int value();
public static Fruit valueOf(int v);
// ...
}

Given the above definitions, we can use enumerated values as follows:

Java
Fruit f1 = Fruit.Apple;
Fruit f2 = Fruit.Orange;
if (f1 == Fruit.Apple) { // Compare with constant
// ...
}
if (f1 == f2) { // Compare two enums
// ...
}
switch (f2) { // Switch on enum
case Fruit.Apple:
// ...
break;
case Fruit.Pear:
// ...
break;
case Fruit.Orange:
// ...
break;
}

The Java mapping includes two methods of interest. The value method returns the Slice value of an enumerator, which is not necessarily the same as its ordinal value. The valueOf method translates a Slice value into its corresponding enumerator, or returns null if no match is found.

In the Fruit definition above, the Slice value of each enumerator matches its ordinal value. This will not be true if we modify the definition to include a custom enumerator value:

Slice
enum Fruit { Apple, Pear = 3, Orange }

The table below shows the new relationship between ordinal value and Slice value:

EnumeratorOrdinalSlice
Apple
0
0
Pear
1
3
Orange
2
4

JavaScript does not have an enumerated type, so a Slice enumeration is emulated using JavaScript objects where each enumerator is an instance of the same type. For example:

Slice
enum Fruit { Apple, Pear, Orange }

The generated code is equivalent to the following JavaScript code:

JavaScript
class Fruit { ... }
Fruit.Apple = new Fruit("Apple", 0);
Fruit.Pear = new Fruit("Pear", 1);
Fruit.Orange = new Fruit("Orange", 2);

And the generated TypeScript definition for the generated code looks like:

TypeScript
class Fruit
{
static readonly Apple:Fruit;
static readonly Pear:Fruit;
static readonly Orange:Fruit;
static valueOf(value:number):Fruit | undefined;
equals(other:any):boolean;
hashCode():number;
toString():string;
readonly name:string;
readonly value:number;
}

Each enumerator defines name and value properties that supply the enumerator's name and ordinal value, respectively. Enumerators also define hashCode, equals and toString methods, and the enumerated type itself defines a valueOf method that converts ordinal values into their corresponding enumerators.

Suppose we modify the Slice definition to include a custom enumerator value:

Slice
enum Fruit { Apple, Pear = 3, Orange }

The generated code changes accordingly:

JavaScript
class Fruit = { ... };
Fruit.Apple = new Fruit("Apple", 0);
Fruit.Pear = new Fruit("Pear", 3);
Fruit.Orange = new Fruit("Orange", 4);

Given the above definitions, we can use enumerated values as follows:

JavaScript
const f1 = Fruit.Apple;
const f2 = Fruit.Orange;
if (f1 === Fruit.Apple) { // Compare with constant
// ...
}
if (f1 === f2) { // Compare two enums
// ...
}
switch(f2) { // Switch on enum
case Fruit.Apple:
// ...
break;
case Fruit.Pear:
// ...
break;
case Fruit.Orange:
// ...
break;
}
// Convert an ordinal value to its enumerator, or undefined if no match
const f = Fruit.valueOf(3);
console.log(f.name + " = " + f.value); // Outputs "Pear = 3"

A Slice enumeration maps to the corresponding enumeration in MATLAB. For example:

Slice
enum Fruit { Apple, Pear, Orange }

The MATLAB mapping for Fruit is shown below:

MATLAB
classdef Fruit < uint8
enumeration
Apple (0)
Pear (1)
Orange (2)
end
methods(Static)
function r = ice_getValue(v)
switch v
case 0
r = Example.Fruit.Apple;
case 1
r = Example.Fruit.Pear;
case 2
r = Example.Fruit.Orange;
otherwise
throw(Ice.MarshalException(...
sprintf('enumerator value %d is out of range', v)));
end
end
end
end

Given the above definitions, we can use enumerated values as follows:

MATLAB
f1 = Fruit.Apple;
f2 = Fruit.Orange;
if f1 == Fruit.Apple % Compare with constant
% ...
end
if f1 == f2 % Compare two enums
% ...
end
switch f2 % Switch on enum
case Fruit.Apple
% ...
case Fruit.Pear
% ...
case Fruit.Orange
% ...
end

You can obtain the ordinal value of an enumerator using the uint8 function:

MATLAB
val = uint8(Fruit.Pear);
assert(val == 1);

To convert an integer into its equivalent enumerator, call the ice_getValue function:

MATLAB
f = Fruit.ice_getValue(2);
assert(f == Fruit.Orange);

This function throws an exception if the given integer does not match any of the enumerators.

The Fruit definition above shows the ordinal values assigned by default to the enumerators. Suppose we modify the definition to include a custom enumerator value:

Slice
enum Fruit { Apple, Pear = 3, Orange }

The generated code changes accordingly:

MATLAB
classdef Fruit < uint8
enumeration
Apple (0)
Pear (3)
Orange (4)
end
...
end

A Slice enumeration is mapped to a PHP class: the name of the Slice enumeration becomes the name of the PHP class; for each enumerator, the class contains a constant with the same name as the enumerator. For example:

Slice
enum Fruit { Apple, Pear, Orange }

The generated PHP class looks as follows:

PHP
class Fruit
{
const Apple = 0;
const Pear = 1;
const Orange = 2;
}

Suppose we modify the Slice definition to include a custom enumerator value:

Slice
enum Fruit { Apple, Pear = 3, Orange }

The generated PHP class changes accordingly:

PHP
class Fruit
{
const Apple = 0;
const Pear = 3;
const Orange = 4;
}

Since enumerated values are mapped to integer constants, application code is not required to use the generated constants. When an enumerated value enters the Ice runtime, Ice validates that the given integer is a valid value for the enumeration. However, to minimize the potential for defects in your code, we recommend using the generated constants instead of literal integers.

A Slice enumeration maps to a Python enum.Enum class. The Slice enum name becomes the Python class name, and each enumerator becomes a class attribute with the same name.

For example:

Slice
enum Fruit { Apple, Pear, Orange }

Generates:

Python
from enum import Enum
class Fruit(Enum):
Apple = 0
Pear = 1
Orange = 2

Each enum member has:

  • value — the underlying Slice integer value.
  • name — the enumerator’s name as a string.
Python
>>> Fruit.Apple.value == 0
True
>>> Fruit(0) == Fruit.Apple
True
>>> Fruit.Apple == 0
False
>>> Fruit.Pear.value
1
>>> Fruit.Pear.name
'Pear'
  • To get a enumerator from its value, use the constructor: Fruit(0). If the value is invalid, Python raises ValueError.
  • To get a member from its name, use item access: Fruit['Apple']. If the name is invalid, Python raises KeyError.

For additional details, see the official Python enum documentation.

A Slice enumeration is emulated using a Ruby class: the name of the Slice enumeration becomes the name of the Ruby class; for each enumerator, the class contains a constant with the same name as the enumerator. For example:

Slice
enum Fruit { Apple, Pear, Orange }

The generated Ruby class looks as follows:

Ruby
class Fruit
include Comparable
Apple = # ...
Pear = # ...
Orange = # ...
def Fruit.from_int(val)
def to_i
def to_s
def <=>(other)
def hash
# ...
end

The compiler generates a class constant for each enumerator that holds a corresponding instance of Fruit. The from_int class method returns an instance given its Slice value, while to_i returns the Slice value of an enumerator and to_s returns its Slice identifier.

Given the above definitions, we can use enumerated values as follows:

Ruby
f1 = Fruit::Apple
f2 = Fruit::Orange
if f1 == Fruit::Apple # Compare for equality
# ...
if f1 < f2 # Compare two enums
# ...
case f2
when Fruit::Orange
puts "found Orange"
else
puts "found #{f2.to_s}"
end

Comparison operators are available as a result of including Comparable, which means a program can compare enumerators according to their Slice values. Note that, when using custom enumerator values, the order of enumerators by their Slice values may not match their order of declaration.

Suppose we modify the Slice definition to include a custom enumerator value:

Slice
enum Fruit { Apple, Pear = 3, Orange }

We can use from_int to examine the Slice values of the enumerators:

Ruby
Fruit::from_int(0) # Apple
Fruit::from_int(1) # nil
Fruit::from_int(3) # Pear
Fruit::from_int(4) # Orange

A Slice enumeration maps to a Swift enumeration that stores raw values of type UInt8 or Int32. For example:

Slice
enum Fruit { Apple, Pear, Orange }

The mapped Swift enumeration is very similar:

Swift
public enum Fruit: UInt8 {
case Apple = 0
case Pear = 1
case Orange = 2
public init() {
self = .Apple
}
}

The raw value type for the generated enumeration is UInt8 when the largest enumerator value is 255 or less; otherwise, the raw value type is Int32.

Suppose we modify the Slice definition to include a custom enumerator value:

Slice
enum Fruit { Apple, Pear = 3, Orange }

The generated Swift definition now includes adjusted values for each enumerators:

Swift
public enum Fruit: UInt8 {
case Apple = 0
case Pear = 3
case Orange = 4
public init() {
self = .Apple
}
}