Go constants

In Go, a constant is a value with a literal representation such as a string of text, Boolean, or numbers. The value for a constant is static and cannot be changed after initial assignment. While the concept they represent is simple, constants, however, have some interesting properties that make them useful, especially when working with numeric values.

Constant literals

Constants are values that can be represented by a text literal in the language. One of the most interesting properties of constants is that their literal representations can either be treated as typed or untyped values. Unlike variables, which are intrinsically bound to a type, constants can be stored as untyped values in memory space. Without that type constraint, numeric constant values, for instance, can be stored with great precision.

The followings are examples of valid constant literal values that can be expressed in Go:

"Mastering Go" 
'G' 
false 
111009 
2.71828 
94314483457513374347558557572455574926671352 1e+500 
5.0i 

Typed constants

Go constant values can be bound to named identifiers using a constant declaration. Similar to a variable declaration, Go uses the const keyword to indicate the declaration of a constant. Unlike variables, however, the declaration must include the literal value to be bound to the identifier, as shown in the following format:

const <identifier list> type = <value list or initializer expressions>

Constants cannot have any dependency that requires runtime resolution. The compiler must be able to resolve the value of a constant at compile time. This means all constants must be declared and initialized with a value literal (or an expression that results to a constant value).

The following snippet shows some typed constants being declared:

const a1, a2 string = "Mastering", "Go" 
const b rune = 'G' 
const c bool = false 
const d int32 = 111009 
const e float32 = 2.71828 
const f float64 = math.Pi * 2.0e+3 
const g complex64 = 5.0i 
const h time.Duration = 4 * time.Second 

golang.fyi/ch02/const.go

Notice in the previous source snippet that each declared constant identifier is explicitly given a type. As you would expect, this implies that the constant identifier can only be used in contexts that are compatible with its types. However, the next section explains how this works differently when the type is omitted in the constant declaration.

Untyped constants

Constants are even more interesting when they are untyped. An untyped constant is declared as follows:

const <identifier list> = <value list or initializer expression>

As before, the keyword const is used to declare a list of identifiers as constants along with their respective bounded values. However, in this format, the type specification is omitted in the declaration. As an untyped entity, a constant is merely a blob of bytes in memory without any type precision restrictions imposed. The following shows some sample declarations of untyped constants:

const i = "G is" + " for Go " 
const j = 'V' 
const k1, k2 = true, !k1 
const l = 111*100000 + 9 
const m1 = math.Pi / 3.141592 
const m2 = 1.414213562373095048801688724209698078569671875376... 
const m3 = m2 * m2 
const m4 = m3 * 1.0e+400 
const n = -5.0i * 3 
const o = time.Millisecond * 5 

golang.fyi/ch02/const.go

From the previous code snippet, the untyped constant m2 is assigned a long decimal value (truncated to fit on the printed page as it goes another 17 digits). Constant m4 is assigned a much larger number of m3 x 1.0e+400. The entire value of the resulting constant is stored in memory without any loss in precision. This can be an extremely useful tool for developers interested in computations where a high level of precision is important.

Assigning untyped constants

Untyped constant values are of limited use until they are assigned to variables, used as function parameters, or are part of an expression assigned to a variable. In a strongly-typed language like Go, this means there is a potential for some type adjustment to ensure that the value stored in the constant can be properly assigned to the target variable. One advantage of using untyped constants is that the type system relaxes the strict application of type checking. An untyped constant can be assigned to different, though compatible, types of different precision without any complaint from the compiler, as shown in the following example:

const m2 = 1.414213562373095048801688724209698078569671875376... 
var u1 float32 = m2 
var u2 float64 = m2 
u3 := m2 

The previous snippet shows the untyped constant m2 being assigned to two variables of different floating-point precisions, u1 and u2, and to an untyped variable, u3. This is possible because constant m2 is stored as a raw untyped value and can therefore be assigned to any variable compatible with its representation (a floating point).

While the type system will accommodate the assignment of m2 to variables of different precision, the resulting assignment is adjusted to fit the variable type, as noted in the following:

u1 = 1.4142135      //float32 
u2 = 1.4142135623730951   //float64 

What about variable u3, which is itself an untyped variable? Since u3 does not have a specified type, it will rely on type inference from the constant value to receive a type assignment. Recall from the discussion in the section Omitting Variable Types earlier, that constant literals are mapped to basic Go types based on their textual representations. Since constant m2 represents a decimal value, the compiler will infer its default to be a float64, which will be automatically assigned to variable u3, as shown:

U3 = 1.4142135623730951  //float64 

As you can see, Go's treatment of untyped raw constant literals increases the language's usability by automatically applying some simple, but effective, type inference rules without sacrificing type-safety. Unlike other languages, developers do not have to explicitly specify the type in the value literal or perform some sort of typecast to make this work.

Constant declaration block

As you may have guessed, constant declarations, can be organized as code blocks to increase readability. The previous example can be rewritten as follows:

const ( 
  a1, a2 string        = "Mastering", "Go" 
  b      rune          = 'G' 
  c      bool          = false 
  d      int32         = 111009 
  e      float32       = 2.71828 
  f      float64       = math.Pi * 2.0e+3 
  g      complex64     = 5.0i 
  h      time.Duration = 4 * time.Second 
... 
) 

golang.fyi/ch02/const2.go

Constant enumeration

One interesting usage of constants is to create enumerated values. Using the declaration block format (shown in the preceding section), you can easily create numerically increasing enumerated integer values. Simply assign the pre-declared constant value iota to a constant identifier in the declaration block, as shown in the following code sample:

const ( 
  StarHyperGiant = iota 
  StarSuperGiant 
  StarBrightGiant 
  StarGiant 
  StarSubGiant 
  StarDwarf 
  StarSubDwarf 
  StarWhiteDwarf 
  StarRedDwarf 
  StarBrownDwarf 
) 

golang.fyi/ch02/enum0.go

The compiler will then automatically do the following:

  • Declare each member in the block as an untyped integer constant value
  • Initialize iota with a value of zero
  • Assign iota, or zero, to the first constant member (StarHyperGiant)
  • Each subsequent constant is assigned an int value increased by one

So the previous list of constants would be assigned a sequence of values going from zero to nine. Whenever const appears as a declaration block, it resets the counter to zero. In the following snippet, each set of constants is enumerated from zero to four separately:

const ( 
  StarHyperGiant = iota 
  StarSuperGiant 
  StarBrightGiant 
  StarGiant 
  StarSubGiant 
) 
const ( 
  StarDwarf = iota 
  StarSubDwarf 
  StarWhiteDwarf 
  StarRedDwarf 
  StarBrownDwarf 
) 

golang.fyi/ch02/enum1.go

Overriding the default enumeration type

By default, an enumerated constant is declared as an untyped integer value. However, you can override the default type of the enumerated values by providing an explicit numeric type for your enumerated constants, as shown in the following code sample:

const ( 
  StarDwarf byte = iota 
  StarSubDwarf 
  StarWhiteDwarf 
  StarRedDwarf 
  StarBrownDwarf 
) 

You can specify any numeric type that can represent integers or floating point values. For instance, in the preceding code sample, each constant will be declared as type byte.

Using iota in expressions

When iota appears in an expression, the same mechanism works as expected. The compiler will apply the expression for each successive increasing value of iota. The following example assigns even numbers to the enumerated members of the constant declaration block:

const ( 
  StarHyperGiant = 2.0*iota 
  StarSuperGiant 
  StarBrightGiant 
  StarGiant 
  StarSubGiant 
) 

golang.fyi/ch02/enum2.go

As you may expect, the previous example assigns an even value to each enumerated constants, starting with 0, as shown in the following output:

    StarHyperGiant = 0    [float64]
    StarSuperGiant = 2    [float64]
    StarBrightGiant = 4   [float64]
    StarGiant = 6         [float64]
    StarSubGiant = 8      [float64]

Skipping enumerated values

When working with enumerated constants, you may want to throw away certain values that should not be part of the enumeration. This can be accomplished by assigning iota to the blank identifier at the desired position in the enumeration. For instance, the following skips the values 0 and 64:

_              = iota    // value 0 
StarHyperGiant = 1 << iota 
StarSuperGiant 
StarBrightGiant 
StarGiant 
StarSubGiant 
_          // value 64 
StarDwarf 
StarSubDwarf 
StarWhiteDwarf 
StarRedDwarf 
StarBrownDwarf 

golang.fyi/ch02/enum3.go

Since we skip iota position 0, the first assigned constant value is at position 1. This results in expression 1 << iota resolving to 1 << 1 = 2. The same is done at the sixth position, where expression 1 << iota returns 64. That value will be skipped and not assigned to any constant, as shown in the following output:

    StarHyperGiant = 2
    StarSuperGiant = 4
    StarBrightGiant = 8
    StarGiant = 16
    StarSubGiant = 32
    StarDwarf = 128
    StarSubDwarf = 256
    StarWhiteDwarf = 512
    StarRedDwarf = 1024
    StarBrownDwarf = 2048

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.86.211