Chapter 7. Composite Types

In prior chapters, you may have caught glimpses of the use of composite types such as arrays, slices, maps, and structs in some of the sample code. While early exposure to these types may have left you curious, rest assured in this chapter you will get a chance to learn all about these composite types. This chapter continues what started in Chapter 4, Data Types, with discussions covering the following topics:

  • The array type
  • The slice type
  • The map type
  • The struct type

The array type

As you would find in other languages, Go arrays are containers for storing sequenced values of the same type that are numerically indexed. The following code snippet shows samples of variables that are assigned array types:

var val [100]int 
var days [7]string 
var truth [256]bool 
var histogram [5]map[string]int 

golang.fyi/ch07/arrtypes.go

Notice the types that are assigned to each variable in the previous example are specified using the following type format:

[<length>]<element_type>

The type definition of an array is composed of its length, enclosed within brackets, followed by the type of its stored elements. For instance, the days variable is assigned a type [7]string. This is an important distinction as Go's type system considers two arrays, storing the same type of elements but with different lengths, to be of different types. The following code illustrates this situation:

var days [7]string 
var weekdays [5]string 

Even though both variables are arrays with elements of type string, the type system considers the days and weekdays variables as different types.

Note

Later in the chapter, you will see how this type restriction is mitigated with the use of the slice type instead of arrays.

Array types can be defined to be multi-dimensions. This is done by combining and nesting the definition of one-dimensional array types as shown in the following snippet:

var board [4][2]int
var matrix [2][2][2][2] byte

golang.fyi/ch07/arrtypes.go

Go does not have a separate type for multi-dimensional arrays. An array with more than one dimension is composed of one-dimensional arrays that are nested within each other. The next section covers how single and multi-dimensional arrays are initialized.

Array initialization

When an array variable is not explicitly initialized, all of its elements will be assigned the zero-value for the declared type of the elements. An array can be initialized with a composite literal value with the following general format:

<array_type>{<comma-separated list of element values>}

The literal value for an array is composed of the array type definition (discussed in the previous section) followed by a set of comma-separated values, enclosed in curly brackets, as illustrated by the following code snippet, which shows several arrays being declared and initialized:

var val [100]int = [100]int{44,72,12,55,64,1,4,90,13,54}
var days [7]string = [7]string{
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
  "Sunday",
}
var truth = [256]bool{true}
var histogram = [5]map[string]int {
  map[string]int{"A":12,"B":1, "D":15},
  map[string]int{"man":1344,"women":844, "children":577,...},
}

golang.fyi/ch07/arrinit.go

The number of elements in the literal must be less than or equal to the size declared in the array type. If the array defined is multi-dimensional, it can be initialized using literal values by nesting each dimension within the enclosing brackets of another, as shown in the following example snippets:

var board = [4][2]int{ 
   {33, 23}, 
   {62, 2}, 
   {23, 4}, 
   {51, 88}, 
} 
var matrix = [2][2][2][2]byte{ 
   {{{4, 4}, {3, 5}}, {{55, 12}, {22, 4}}}, 
   {{{2, 2}, {7, 9}}, {{43, 0}, {88, 7}}}, 
} 

golang.fyi/ch07/arrinit.go

The following snippet shows two additional ways that array literals can be specified. The length of an array may be omitted and replaced by ellipses during initialization. The following will assign type [5]string to variable weekdays:

var weekdays = [...]string{ 
   "Monday", 
   "Tuesday", 
   "Wednesday", 
   "Thursday", 
   "Friday",    
}  

The literal value of an array can also be indexed. This is useful if you want to initialize only certain array elements while allowing others to be initialized with their natural zero-value. The following specifies the initial values for elements at positions 0, 2, 4, 6, 8. The remaining elements will be assigned the empty string:

var msg = [12]rune{0: 'H', 2: 'E', 4: 'L', 6: 'O', 8: '!'} 

Declaring named array types

The type of an array can become awkward for reuse. For each declaration, it becomes necessary to repeat the declaration, which can be error prone. The way to handle this idiomatically is to alias array types using type declarations. To illustrate how this works, the following code snippet declares a new named type, matrix, using a multi-dimension array as its underlying type:

type matrix [2][2][2][2]byte 
 
func main() { 
   var mat1 matrix 
   mat1 = initMat() 
   fmt.Println(mat1) 
} 
 
func initMat() matrix { 
   return matrix{ 
         {{{4, 4}, {3, 5}}, {{55, 12}, {22, 4}}}, 
         {{{2, 2}, {7, 9}}, {{43, 0}, {88, 7}}}, 
   } 
} 

golang.fyi/ch07/arrtype_dec.go

The declared named type, matrix, can be used in all contexts where its underlying array type is used. This allows a simplified syntax that promotes reuse of the complex array type.

Using arrays

Arrays are static entities that cannot grow or shrink in size once they are declared with a specified length. Arrays are a great option when a program needs to allocate a block of sequential memory of a predefined size. When a variable of an array type is declared, it is ready to be used without any further allocation semantics.

So the following declaration of the image variable would allocate a memory block composed of 256 adjacent int values initialized with zeroes, as shown in the following figure:

var image [256]byte
Using arrays

Similar to C and Java, Go uses the square brackets index expression to access values stored in an array variable. This is done by specifying the variable identifier followed by an index of the element enclosed within the square brackets, as shown in the following code sample:

p := [5]int{122,6,23,44,6} 
p[4] = 82 
fmt.Println(p[0]) 

The previous code updates the fifth element and prints the first element in the array.

Array length and capacity

The built-in len function returns the declared length of an array type. The built-in cap function can be used on an array to return its capacity. For instance, in the following source snippet, the array seven of type [7]string will return 7 as its length and capacity:

func main() { 
   seven := [7]string{"grumpy", "sleepy", "bashful"} 
   fmt.Println(len(seven), cap(seven)) 
} 

For arrays, the cap() function always returns the same value as len(). This is because the maximum capacity of an array value is its declared length. The capacity function is better suited for use with the slice type (discussed later in the chapter).

Array traversal

Array traversal can be done using the traditional for statement or with the more idiomatic for…range statement. The following snippet of code shows array traversal done with both the for statement, to initialize an array with random numbers in init(), and the for range statement used to realize the max() function:

const size = 1000 
var nums [size]int 
 
func init() { 
   rand.Seed(time.Now().UnixNano()) 
   for i := 0; i < size; i++ { 
         nums[i] = rand.Intn(10000) 
   } 
} 

func max(nums [size]int) int { 
   temp := nums[0] 
   for _, val := range nums { 
         if val > temp { 
               temp = val 
         } 
   } 
   return temp 
} 

golang.fyi/ch07/arrmax_iter.go

In the traditional for statement, the loop's index variable i is used to access the value of the array using the index expression num[i]. In the for…range statement, in the max function, the iterated value is stored in the val variable with each pass of the loop and the index is ignored (assigned to the blank identifier). If you do not understand how for statements work, refer to Chapter 3, Go Control Flow, for a thorough explanation of the mechanics of loops in Go.

Array as parameters

Arrays values are treated as a single unit. An array variable is not a pointer to a location in memory, but rather represents the entire block of memory containing the array elements. This has the implications of creating a new copy of an array value when the array variable is reassigned or passed in as a function parameter.

This could have unwanted side effects on memory consumption for a program. One fix for is to use pointer types to reference array values. In the following example, a named type, numbers, is declared to represent array type [1024 * 1024]]int. Instead of taking the array value directly as parameters, functions initialize() and max() receive a pointer of type *numbers, as shown in the following source snippet:

type numbers [1024 * 1024]int 
func initialize(nums *numbers) { 
   rand.Seed(time.Now().UnixNano()) 
   for i := 0; i < size; i++ { 
         nums[i] = rand.Intn(10000) 
   } 
} 
func max(nums *numbers) int { 
   temp := nums[0] 
   for _, val := range nums { 
         if val > temp { 
               temp = val 
         } 
   } 
   return temp 
} 
func main() { 
   var nums *numbers = new(numbers) 
   initialize(nums) 
} 

golang.fyi/ch07/arrptr.go

The previous code uses the built-in function new(numbers) to initialize the array elements with their zero values and obtain a pointer to that array as shown in main(). So when the functions initialize and max are invoked, they will receive the address (a copy of it) of the array instead of the entire 100K-sized array.

Before changing the subject, it should be noted that a composite literal array value can be initialized with the address operator & to initialize and return a pointer for the array, as shown in the following example. In the snippet, composite literal &galaxies{...} returns pointer *galaxies, initialized with the specified element values:

type galaxies [14]string 
func main() { 
   namedGalaxies = &galaxies{ 
         "Andromeda", 
         "Black Eye", 
         "Bode's", 
          ...   
   } 
   printGalaxies(namedGalaxies) 
} 

golang.fyi/ch07/arraddr.go

The array type is a low-level storage construct in Go. Arrays, for instance, are usually used as the basis for storage primitives, where there are strict memory allocation requirements to minimize space consumption. In more common cases however, the slice, covered in the next section, is often used as the more idiomatic way of working with sequenced indexed collections.

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

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