Go Arrays: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(119 intermediate revisions by the same user not shown)
Line 1: Line 1:
=External=
* https://go.dev/ref/spec#Array_types
=Internal=
=Internal=
* [[Go_Language#Array|Go Language]]
* [[Go_Slices#Overview|Go Slices]]
* [[Go_Maps#Overview|Go Maps]]


* [[Go Concepts - The Type System#Built-in_Types|Go Built-in Types]]
=Overview=
An array is a [[Go_Language#Composite_Types|composite type]] that consists in a fixed-length, contiguous and numbered sequence of elements. All elements of an array have '''the same type'''. The type of the elements can be a Go built-in type, a user type or a pointer.  <span id='Pointer_Array'></span><code>[[Go_Language#Star_Operator|*<type>]]</code> notation is used to declare a pointer array.
 
Arrays are useful when planning a detailed layout of memory and sometimes can help avoid allocation, but primarily they are a building block for [[Go_Slices#Overview|slices]].
 
The compiler knows the length of the array and the type of its elements at compile time, so it knows how much space to allocate. <span id='Length_of_an_array_is_part_of_the_type'></span>The length of the array is part of the type name, arrays with different lengths are of different types, even if the element type is the same. Once an array is declared, its type and length cannot be changed. The types <code>[10]int</code> and <code>[20]int</code> are two distinct types.
 
Arrays are fast: data is maintain in continuous blocks of memory, which keeps them in the CPU caches longer. Index access and arithmetic is fast: the type information and the guarantee that all elements are of same type allows calculating "distance in memory" to find elements. The arrays are the internal data structures used both by [[Go_Slices#Overview|slices]] and [[Go_Maps#Overview|maps]], and they are rarely used directly in Go code. Because the function arguments are passed by value, slices should be used instead of arrays for performance reasons, explained [[Go_Slices#Slice_Performance|here]].


=Overview=
An array variable is a value, not a [[Go_Language#.28No.29_Reference_Variables|reference variable]], which means that no two different array variables may point to the same array instance. Assigning an array to another copies all the elements. If you pass an array to a function, the function will receive a copy of the array, not a pointer to it.
 
=Arrays are Values=
An array is a value in Go. It can be used in an assignment operation. The variable name denotes the entire array, thus an array can be assigned to other arrays of the same type, which is designated by the length and element type. When an array <code>a2</code> is assigned to an array <code>a1</code>, the values of <code>a2</code> are copied across to <code>a1</code>.
 
In Go, function arguments are [[Go_Functions#Pass_by_Value_vs._Pass_by_Reference|passed by value]]. The fact that arrays are values has implications on how arrays should be passed between functions. If an array is passed as argument, a copy of the entire array is made and placed on the stack, regardless of the array's size. For large arrays, this can have space and performance implications.
 
<span id='Pass_by_Reference'></span>If you must work with arrays, passing pointers to arrays is preferred. However, passing pointers comes with [[Go_Functions#Pass_by_Reference|complicated syntax]]:
<syntaxhighlight lang='go'>
func passArrayByPointerExample(a *[3]int) {
    (*a)[1] = (*a)[1] + 1
}
 
func callerFunction() {
    ia := [3]int{10, 20, 30}
    passArrayByPointerExample(&ia)
    fmt.Println(ia) // will print [10, 21, 30]
}
</syntaxhighlight>
 
A better, Go idiomatic alternative is to avoid using arrays directly in code, and use [[Go_Slices#Passing_Slices_as_Arguments|slices]] instead.


An array is a numbered sequence of elements, ''of a single type'', and ''with a fixed length''.
Also see: {{Internal|Go_Slices#Passing_Slices_as_Arguments|Passing Slices as Arguments}}


=Declaration=
=Declaration=
The array type includes the size, enclosed in square brackets, followed by the element type. As mentioned [[#Overview|above]], different size arrays are different types, even if they have the same element type.
<syntaxhighlight lang='go'>
var a [7]int
</syntaxhighlight>


<pre>
The elements are initialized to zero value of the type the array is composed of, upon declaration:
var a [5]int
<syntaxhighlight lang='go'>
</pre>
println(a[0]) // displays 0
 
</syntaxhighlight>
A declaration without explicit initialization initializes the array with the [[Go Concepts - The Type System#Zero_Value|type's zero value]].


Type inference and initialization declaration:
The following declaration designates a [[Go_Slices#Declaration|slice]], NOT an array:
<syntaxhighlight lang='go'>
var s []int
</syntaxhighlight>


<pre>
=Literals=
a := [5]int{1, 2, 3, 4, 5}
The array composite literals are predefined values that go into an array and they are used to initialize an array. The type (<code>[3]int</code>) is specified as part of the array literal, so it does not have to be provided in the <code>var</code> syntax, but it can be provided, redundantly.
</pre>
<syntaxhighlight lang='go'>
var a = [3]int{7, 17, 37}
</syntaxhighlight>
The length of the array, provided between square brackets, must match the number of elements in the literal, otherwise the compiler will indicate compilation error.


=Array Literals=
A better, alternative representation of the array literal uses <code>[...]</code> to express that the size of the array will be given by the number of elements in the literal.
<syntaxhighlight lang='go'>
var a = [...]int{7, 17, 37} // the type of a is inferred to be [3]int


<pre>
var b = [...]int{
a := [5]int{1, 2, 3, 4, 5}
    5,
</pre>
    15,
    25,
}
</syntaxhighlight>
The extra trailing comma is required when the elements are specified on separate lines, but not when they're specified in a single line. This is to allow commenting out elements without breaking the program.
==Partial Initialization Literals==
Go provides syntax for initializing just some of the elements with specified values, letting the compiler to initialize the rest with zero values:
<syntaxhighlight lang='go'>
a := [100]int{0:10, 1:20}
</syntaxhighlight>
In the example above, the element 0 is initialized to 10, the element 1 is initialized to 20 and the rest are initialized to their zero value (0).


<pre>
=Initialization=
a := [5]int{
The [[Go_Language#Variable_Initialization_with_Type_Inference|initialization with type inference]], with a [[#Literal|literal]], can be used:
        1,
<syntaxhighlight lang='go'>
        2,  
var a = [...]int{7, 17, 37}
        3,
</syntaxhighlight>
        4,  
Also, the [[Go_Language#Short_Variable_Declaration|short variable declaration]] can be used as well:
        5,
<syntaxhighlight lang='go'>
  }
a := [...]int{7, 17, 37}
</pre>
</syntaxhighlight>


The extra trailing comma is ''required'' when the elements are specified on separate lines, but not when they're specified in a single line.
=Empty Array=
=Operators=
==Indexing Operator <tt>[]</tt>==
Array elements are accessed, and can be modified using the [[Go_Language#Indexing_Operator|indexing (subscript) operator]]. Indices are zero-based. Accesses are bound-checked and an attempt to access an element out of bounds produces a compile-time or runtime error:


=Array Operators and Functions=
Compile-time error:
<font size=-2>
./main.go:13: invalid array index 3 (out of bounds for 3-element array)
</font>


==Indexing Operator==
Runtime error:
<font size=-2>
panic: runtime error: index out of range
</font>


Indexing operator <tt>[[Go Concepts - Operators#.5B.5D|[]]]</tt> returns the value at that position.
=Iterating though Arrays=
Use the <code>[[Go_Keyword_range|range]]</code> [[Go_Language#Keywords|keyword]]. <code>[[Go_Keyword_range|range]]</code> allows to iterate by indices, values or both.
Iterate by indices:
<syntaxhighlight lang='go'>
for i := range a {
  fmt.Printf("index: %d\n", i)
}
</syntaxhighlight>


==Array Length==
Iterate by values:
<syntaxhighlight lang='go'>
for _, e := range a {
  fmt.Printf("element value: %d\n", e)
}
</syntaxhighlight>
Iterate by both indices and values:
<syntaxhighlight lang='go'>
for i, e := range a {
  fmt.Printf("index: %d, element value: %d\n", i, e)
}
</syntaxhighlight>


<tt>[[Go Built-In Functions Length and Capacity#len.28.29|len()]]</tt>
=TO DISTRIBUTE=
===Array Length===
<tt>[[Go Built-In Functions Length and Capacity#len.28.29|len()]]</tt> returns the length of the array.
===Iterating over Arrays===
<blockquote style="background-color: #f9f9f9; border: solid thin lightgrey;">
:[[Go Keyword range|<tt>range</tt> Keyword]]
</blockquote>
==Multidimensional Arrays==
<font color=red>'''TODO''' Go in Action page 84.</font>
</font>

Latest revision as of 02:04, 19 August 2024

External

Internal

Overview

An array is a composite type that consists in a fixed-length, contiguous and numbered sequence of elements. All elements of an array have the same type. The type of the elements can be a Go built-in type, a user type or a pointer. *<type> notation is used to declare a pointer array.

Arrays are useful when planning a detailed layout of memory and sometimes can help avoid allocation, but primarily they are a building block for slices.

The compiler knows the length of the array and the type of its elements at compile time, so it knows how much space to allocate. The length of the array is part of the type name, arrays with different lengths are of different types, even if the element type is the same. Once an array is declared, its type and length cannot be changed. The types [10]int and [20]int are two distinct types.

Arrays are fast: data is maintain in continuous blocks of memory, which keeps them in the CPU caches longer. Index access and arithmetic is fast: the type information and the guarantee that all elements are of same type allows calculating "distance in memory" to find elements. The arrays are the internal data structures used both by slices and maps, and they are rarely used directly in Go code. Because the function arguments are passed by value, slices should be used instead of arrays for performance reasons, explained here.

An array variable is a value, not a reference variable, which means that no two different array variables may point to the same array instance. Assigning an array to another copies all the elements. If you pass an array to a function, the function will receive a copy of the array, not a pointer to it.

Arrays are Values

An array is a value in Go. It can be used in an assignment operation. The variable name denotes the entire array, thus an array can be assigned to other arrays of the same type, which is designated by the length and element type. When an array a2 is assigned to an array a1, the values of a2 are copied across to a1.

In Go, function arguments are passed by value. The fact that arrays are values has implications on how arrays should be passed between functions. If an array is passed as argument, a copy of the entire array is made and placed on the stack, regardless of the array's size. For large arrays, this can have space and performance implications.

If you must work with arrays, passing pointers to arrays is preferred. However, passing pointers comes with complicated syntax:

func passArrayByPointerExample(a *[3]int) {
    (*a)[1] = (*a)[1] + 1
}

func callerFunction() {
    ia := [3]int{10, 20, 30}
    passArrayByPointerExample(&ia)
    fmt.Println(ia) // will print [10, 21, 30]
}

A better, Go idiomatic alternative is to avoid using arrays directly in code, and use slices instead.

Also see:

Passing Slices as Arguments

Declaration

The array type includes the size, enclosed in square brackets, followed by the element type. As mentioned above, different size arrays are different types, even if they have the same element type.

var a [7]int

The elements are initialized to zero value of the type the array is composed of, upon declaration:

println(a[0]) // displays 0

The following declaration designates a slice, NOT an array:

var s []int

Literals

The array composite literals are predefined values that go into an array and they are used to initialize an array. The type ([3]int) is specified as part of the array literal, so it does not have to be provided in the var syntax, but it can be provided, redundantly.

var a = [3]int{7, 17, 37}

The length of the array, provided between square brackets, must match the number of elements in the literal, otherwise the compiler will indicate compilation error.

A better, alternative representation of the array literal uses [...] to express that the size of the array will be given by the number of elements in the literal.

var a = [...]int{7, 17, 37} // the type of a is inferred to be [3]int

var b = [...]int{
    5,
    15,
    25,
 }

The extra trailing comma is required when the elements are specified on separate lines, but not when they're specified in a single line. This is to allow commenting out elements without breaking the program.

Partial Initialization Literals

Go provides syntax for initializing just some of the elements with specified values, letting the compiler to initialize the rest with zero values:

a := [100]int{0:10, 1:20}

In the example above, the element 0 is initialized to 10, the element 1 is initialized to 20 and the rest are initialized to their zero value (0).

Initialization

The initialization with type inference, with a literal, can be used:

var a = [...]int{7, 17, 37}

Also, the short variable declaration can be used as well:

a := [...]int{7, 17, 37}

Empty Array

Operators

Indexing Operator []

Array elements are accessed, and can be modified using the indexing (subscript) operator. Indices are zero-based. Accesses are bound-checked and an attempt to access an element out of bounds produces a compile-time or runtime error:

Compile-time error:

./main.go:13: invalid array index 3 (out of bounds for 3-element array)

Runtime error:

panic: runtime error: index out of range

Iterating though Arrays

Use the range keyword. range allows to iterate by indices, values or both. Iterate by indices:

for i := range a {
  fmt.Printf("index: %d\n", i)
}

Iterate by values:

for _, e := range a {
  fmt.Printf("element value: %d\n", e)
}

Iterate by both indices and values:

for i, e := range a {
  fmt.Printf("index: %d, element value: %d\n", i, e)
}

TO DISTRIBUTE

Array Length

len() returns the length of the array.

Iterating over Arrays

range Keyword

Multidimensional Arrays

TODO Go in Action page 84.