Complex types in the Go language are described in detail

  • 2020-05-05 11:22:03
  • OfStack

The golang composite types include: structs, arrays, slices, and Maps.

1, array

Array

Arrays in golang are very different from arrays in C and more like arrays in Pascal. (Slice, next topic, something like an array in C)


var ar [3]int

Declare ar to be an array of three integers, with all elements initialized to 0.

Size is a component of a type.

The built-in function len can be used to get the array size:


len(ar) = 3

The array is of value type

Arrays in golang are values, not implicit Pointers in C. You can get the address of the array and generate a pointer to the array (for example, pass it efficiently to a function) :


func f(a [3]int) { fmt.Println(a) }  
func fp(a *[3]int) { fmt.Println(a) }  
 
func main() {  
    var ar [3] int 
    f(ar) // Pass a ar A copy of the   
    fp(&ar) // Pass a point ar A pointer to the   

Output:


[0 0 0] &[0 0 0]

array literals

All conformance types have the same value creation syntax. Take arrays, for example, with the syntax:

An array of three integers:


[3]int{1, 2, 3}

An array of 10 integers whose first three elements are not 0:


[10]int{ 1, 2, 3}

Don't want to count? The use of... Represents length:


[...]int{1, 2, 3}

Don't want to initialize all the values? Use key:value versus:


[10]int{2:1, 3:1, 5:1, 7:1}

pointer

to the array literal

You can get the address of the array literal and get a pointer to the new array instance:


func fp(a *[3]int) { fmt.Println(a) }  
func main() {  
    for i := 0; i < 3; i++ {  
        fp(&[3]int{i, i*i, i*i*i})  
    }  

Output:


&[0 0 0]
&[1 1 1]
&[2 4 8]

2, slice (Slice)

Slice

A slice is a reference to a segment of an array.

Slices are used more and more widely than regular arrays.

Slicing is cheap to use.

A slice type is much like an array type with no size:


var a []int

The built-in len(a) returns the number of elements in the slice.

By "slicing" an array or slice, we can create a new slice:


a = ar[7:9]

The valid subvalues of a(a in the above example) are 0 and 1; len (a) = = 2.

slicing shorthand

When slicing an array, the first TAB defaults to 0:

ar[:n] is equivalent to a[0:n].

The second lower value defaults to len(array/slice) :

ar[n:] is equivalent to ar[n:len(ar)].

So when slicing by the array:

ar[:] is equivalent to ar[0:len(ar)].

slice reference array

Conceptually:


type Slice struct {
base *elemType // Point to the 0th Pointer to element
len int // The number of elements in a slice
cap int // A slice can contain the number of elements
}

Array:


ar : 7 1 5 4 3 8 7 2 11 5 3

Chip:


len(ar) = 3
4

creates slices

The slice literal looks like an array literal with no specified size:


len(ar) = 3
5

The code above creates an array of length 5 and creates a slice to reference the array.

We can use the built-in make function to assign a slice (the underlying function is actually an array) :


len(ar) = 3
6

Why make instead of new? Because we need to create slices, not just to allocate memory. Note that make([]int, 10) returns []int, while new([]int) returns *[]int.

Create slices using make, map, and channel.

slice capacity

A slice is a reference to the underlying array. So there are elements in the array that are not in the range of the slice reference.

The built-in function cap(capacity) reports how long a slice may grow.


len(ar) = 3
7

len(a) = 2, cap(a) = 5, now we can slice again:


len(ar) = 3
8

len(a) is now 4, while cap(a) is still 5.

adjust slice size

Slices can be used as growable arrays. Assign a slice using make and specify its length and capacity. When we want to grow, we can do a re-slice:


len(ar) = 3
9

Thus, the length of sl is always the number of elements, but its capacity can be increased as needed.

This technique is very inexpensive and is a common practice in Go.

Slicing is very cheap to use

You are free to distribute and adjust the slice size as needed. They are passed on at a small cost; You don't have to assign.

Remember that they are references, so the underlying storage can be modified.

For example, I/O USES slicing instead of counting:


func Read(fd int, b []byte) int 
var buffer [100]byte  
    for i := 0; i < 100; i++ {  
    // Every time to Buffer Is filled with a byte   
    Read(fd, buffer[i:i+1]) // no allocation here  

Split an Buffer:


header, data := buf[:n], buf[n:]

Strings can also be sliced, and with similar efficiency.

3, Maps

maps

Map is another reference type. They are declared like this:


var m map[string]float64

An map is declared here, with the index key of type string and the value type float64. This is similar to the type in C++ *map< string, float64 > .

For a given map m, len(m) returns the number of key.

map creation

As with creating a slice, an map variable is an empty reference; You should put something into it before you can use it.

Three ways:

1) literal: comma-separated key:value pair list


m = map[string]float64{"1":1, "pi":3.1415}

2) create

m = make(map[string]float64) // make not new

3) assignment

var m1 map[string]float64
m1 = m // m1 and m Now quote the same map

map index

(the next few examples all use:


m = map[string]float64{"1":1, "pi":3.1415})

Access an element; If the element does not exist, you get the zero value of the corresponding map value type:

one := m["1"]
zero := m["not present"] // zero Be set to 0.0.

Set the value of an element (setting it twice will update it to the latest value)

m["2"] = 2
m["2"] = 3 // confusion

tests for existence

To test the existence of an key in an map, we can use a form of "comma, om" with multiple assignments:


m = map[string]float64{"1":1, "pi":3.1415} var value float64
var present bool value, present = m[x]

Or as usual:


value, ok := m[x] // "comma ok" In the form of

If x and key exist in map, the Boolean variable will be set to true; value will be assigned the corresponding value of key in map. Instead, the Boolean variable is set to false, and value is set to zero of the corresponding value type.

delete

A value in map,

, can be deleted using a multivariate assignment


[0 0 0] &[0 0 0]
1

If the value of keep is true, then v is assigned to map. If keep is false, key x in map is deleted. So delete an key:


[0 0 0] &[0 0 0]
2

The deletion method described above in Go 1 has been removed and delete(m, x) has been replaced.

for and range

For arrays, slices, and map (and more types we'll see in part 3), the for loop provides a special syntax for iterating through its elements.


[0 0 0] &[0 0 0]
3

With just one variable, we can get key:


[0 0 0] &[0 0 0]
4

Variables can be assigned or declared with :=.

For arrays and slices, this way we get the index of the element and the value of the element.

USES range for the string

When for range is used for strings, the actual element iterated is the Unicode code point (code point), not the byte (for bytes, you can use []byte or the standard for statement). We assume the string package

Contains characters encoded with UTF-8.

Next loop:


[0 0 0] &[0 0 0]
5

Output: 0:'[' 1:'ÿ' 3:' bound '6:']'

If the wrong UTF-8 code point is encountered, the character is set to U+FFFD, with the subscript moved back one byte.

4, Structs

structs

You should be familiar with struct in Go: simple data field declarations.


[0 0 0] &[0 0 0]
6

More commonly used:


[0 0 0] &[0 0 0]
7

struct allows programmers to define memory layouts.

struct is the value type

struct is the value type, new(StructType) returns a pointer to a zero value (allocated memory is set to zero).


type Point struct {
x, y float64
}
var p Point
p.x = 7
p.y = 23.4
var pp *Point = new(Point)
*pp = p
pp.x = Pi // (*pp).x The syntactic sugar

For struct Pointers, there is no -> Symbols are available. Go provides an indirect approach.

creates the structure

Structs are value types, so you can create a full 0 struct variable just by declaring it.

You can also use new to create a structure.


var p Point // Zero value
pp := new(Point) // idioms

The struct literal syntax is also as expected:


p = Point{7.2, 8.4}
p = Point{y:8.4, x:7.2}
pp = &Point{7.2, 8.4} // idioms
pp = &Point{} // It's also customary, == new(Point)

As with arrays, you get the address of the structure literal, and you get the address of the new structure.

These examples are constructors.

export type and field

The structure can only be visible outside the package if the first letter of the name of the field of the structure (and method, which we will cover) is capitalized.

Private types and fields:


type point struct { x, y float64 }

Export type and field:

[3]int{1, 2, 3}
2
Export type and private type mixed fields:

type Point struct {
X, Y float64 // exported
name string // not exported
}

You can even create a private type with an export field. Exercise: when will it come in handy?

anonymous field

In a structure, you can declare fields without names, such as another structure type. These fields are called anonymous fields. They look like inner structures simply inserted or "embedded" into the

Like the outer structure.

This simple mechanism provides a way to inherit existing implementations from other types.

Here's an example.

An anonymous struct field :


type A struct {
ax, ay int
} type B struct {
A
bx, by float64
}

B looks like it has four fields ax, ay, bx, and by. B can be viewed as {ax, ay int; bx, by float64}.

Then the literal value of B must provide details:


b := B{A{1, 2}, 3.0, 4.0}
fmt.Println(b.ax, b.ay, b.bx, b.by)

Output 1, 2, 3, 4

The anonymous field takes the type as the name

Anonymous fields are more than simply inserting them, they have more meaning: B also has the field A. An anonymous field looks like a field whose name is its type name.


b := B{A{ 1, 2}, 3.0, 4.0}
fmt.Println(b.A)

Output: {1, 2}. If A comes from another package, this field is still called A.


import "pkg"
type C struct { pkg.A }
...
c := C {pkg.A{1, 2}}
fmt.Println(c.A) // not c.pkg.A

anonymous field of any type

Any named type or pointer to a named type can be used as an anonymous field. They can appear anywhere in the structure.


type C struct {
x float64
int
string
}
c := C{3.5, 7, "hello"}
fmt.Println(c.x, c.int, c.string)

Output: 3.5 7 hello

conflicts and veils

If there are two fields with the same name (possibly the name of an inheritance type), the code follows the following rule:

1) cover the name of the outer layer with the name of the inner layer. This provides a way to override fields/methods.
2) if the same name appears at the same level, it will be an error if the name is used. (no errors if not used)

Ambiguity is something that no rule can solve and must be fixed.

conflict example


type A struct { a int }
type B struct { a, b int }
type C struct { A; B }
var c C

An error will occur using c.a. Is it c. A. a or c. B. a?


type D struct { B; b float64 }
var d D

Using d. b no problem: it is float64 type variable, not d. B. b. To get the inner b, available d. B. b.


Related articles: