# 08. Type System

### Primitive types

* **Boolean**
* **Numeric**
* **String**

Because of this, when adding or removing something from a value of one of these types, a new value should be created. Based on this, when passing values of these types to functions and methods, a copy of the value should be passed.

### Structured types

Составные типы образуют более сложные типы путем составления более простых. Для structured типов Go сразу выделяет память, даже если никакие значения не указаны. Поэтому для них всегда существует "нулевое" значение. Structured типы передаются по значению и всегда копируются. Их можно сравнить друг с другом. Не могут иметь `nil` значений.

* **arrays**
* **structs**

### Reference types

Ссылочные типы в Go:

* **pointers**
* **slice,**
* **map**,
* **channel**,
* **function types**,
* **interface types**

Общим для ссылочных типов является то, что все они обращаются к своим значениям косвенно, так что результат действия операции наблюдается на всех ссылках на это значение.

* When you declare a variable from one of these types, the value that’s created is called a *header value*.
* Ссылочные типы могут иметь `nil` значение.
* All the different header values from the different reference types contain a pointer to an underlying data structure.
* Each reference type also contains a set of unique fields that are used to manage the underlying data structure.
* You never share reference type values because the header value is designed to be copied.

### Default type value

* Number = `0`
* Boolean = `false`
* String = `""` (empty string)
* Reference types `nil`

### Comparable types

Comparable types are

* boolean,
* numeric,
* string,
* pointer,
* channel,
* interface types,
* structs or arrays that contain only those types

&#x20;Go doesn't support comparisons (with the `==` and `!=` operators) between values of the following types:

* slice types
* map types
* function types
* any struct type with a field whose type is incomparable and any array type which element type is incomparable.

Compilers forbid comparing two values of incomparable types.

Slices, maps, and functions cannot be compared using `==`, only against `nil`.

![](/files/-M_H71pC8UACDIV9RJmK)

Types which values can be used as arguments of built-in `len` function (and `cap`, `close`, `delete`, `make` functions)

![](/files/-M_MsOAf5aLaMwwYCIpc)

## `nil` value

In Go, `nil` can represent zero values of the following kinds of types:

* pointer types (including type-unsafe ones).
* map types.
* slice types.
* function types.
* channel types.
* interface types.

## Type conversions

Unlike in C, in Go assignment between items of different type requires an explicit conversion.

The expression `T(v)` converts the value `v` to the type `T`.&#x20;

Преобразования из одного типа в другой разрешено, если они оба имеют один и тот же базовый тип, или если оба являются неименованными указателями на переменные одного и того же базового типа. Такие преобразования изменяют тип, но не представление самого значения.

```go
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

// Or more simply
i := 42
f := float64(i)
u := uint(f)
```

```go
func main() {
    var x, y int = 3, 4
    var f float64 = math.Sqrt(float64(x*x + y*y))
    var z uint = uint(f)
    fmt.Println(x, y, z) // 3 4 5
}
```

```go
var string = "magic"
var bytes = []byte(string)
fmt.Println(bytes)
```

### Conversions of user defined types

Операторы сравнения могут использоваться для сравнения значения именованного типа с другим значением того же типа, или со значением неименованного типа с тем же базовым типом. Но два значения разных именованных типов сравнивать непосредственно нельзя.

```go
type Celsius float64
type Farenheit float64

var c Celsius
var f Farenheit

c == f // compile time error
var val = Celsius(f) // Ok
```

### Invalid conversions

Conversion can be done only if Go compiler is able to check its correctness. Scenarios where it isn’t verifiable at compile-time are as follows:

1. interface type → concrete type

```go
type I interface {
    M()
}
type T struct {}
func (T) M() {}
func main() {
    var v I = T{}
    fmt.Println(T(v)) // error
}
```

1. interface type → interface type

```go
type I1 interface {
    M()
}
type I2 interface {
    M()
    N()
}
func main() {
    var v I1
    fmt.Println(I2(v))
}
```

## Type expression

Объявление `type` определяет новый именованный тип, который имеет тот же базовый тип, что и существующий. Именованный тип позволяет отличать различные, и возможно несовместимые, использования базового типа, с тем, что бы они не оказались непреднамеренно смешанными.

```
type Duration int64
```

```go

func main() {
    var dur Duration
    // dur = int64(1000) compiler error
    dur = Duration(122)
}
```

Even though `int64` is acting at the base type for `Duration`, it doesn’t mean Go considered them to be the same. `Duration` and `int64` are two distinct and different types, and `Duration` is still its own unique type. Values of two different types can’t be assigned to each other, even if they’re compatible. The compiler doesn’t implicitly convert values of different types.

## Type Declaration

* a new defined type and its respective source type in type definitions are two distinct types.
* two types defined in two type definitions are always two distinct types.
* the new defined type and the source type will share the same underlying type (see below for the definition of underlying types), and their values can be converted to each other.
* types can be defined within function bodies.

```go
// The following new defined and source types
// are all basic types.
type (
	MyInt int
	Age   int
	Text  string
)

// The following new defined and source types are
// all composite types.
type IntPtr *int
type Book struct{author, title string; pages int}
type Convert func(in0 int, in1 bool)(out0 int, out1 string)
type StringArray [5]string
type StringSlice []string

func f() {
	// The names of the three defined types
	// can be only used within the function.
	type PersonAge map[string]int
	type MessageQueue chan string
	type Reader interface{Read([]byte) int}
}
```

According to Go assignability, a value of type `Name1` is assignable to `map[string]string` (because the latter is not a named type) but a value of type `Name1` is not assignable to `Name2` (because both are named types, and the names differ).&#x20;

## Type Aliasing

* Type alias declaration, which introduces an alternate name for an existing type.
* A type name (or literal) and its aliases all denote an identical type. By the above declarations, `Name` is an alias of `string`, so both denote the same type.

```go
type (
	Name = string
	Age  = int
)

type table = map[string]int
type Table = map[Name]Age
```

In this example, because Alias is an alternate spelling for `map[string]string`, a value of type `Name1` is assignable to `Alias` (because Alias is the same as `map[string]string`, which is not a named type).

## Type `switch`

A type `switch` is a construct that permits several type assertions in series.

* A type `switch` is like a regular `switch` statement, but the cases in a type `switch` specify types (not values), and those values are compared against the type of the value held by the given interface value.
* `x.(type)` можно использовать только в type switch.
* Cannot type `switch` on non-interface value
* The declaration in a type `switch` has the same syntax as a type assertion `i.(T)`, but the specific type `T` is replaced with the keyword `type`.

Example:

```go
switch i.(type) {
case T:
    // here v has type T
case S:
    // here v has type S
default:
    // no match; here v has the same type as i
}
```

### Implementation

* This `switch` statement tests whether the interface value `i` holds a value of type `T` or `S`.
* In each of the `T` and `S` cases, the variable `v` will be of type `T` or `S` respectively and hold the value held by `i`.
* In the `default` case (where there is no match), the variable `v` is of the same interface type and value as `i`.

```go
whatAmI := func(i interface{}) {
    switch i.(type) {
    case bool:
        fmt.Println("I'm a bool")
    case int:
        fmt.Println("I'm an int")
    default:
        fmt.Printf("Don't know type")
    }
}

whatAmI(true)   // I'm a bool
whatAmI(1)      // I'm an int
whatAmI("hey")  // Don't know type
```

### Optional simple statement

Guard can be preceded with simple statement like another short variable declaration:

```go
var v I1 = T1{}
switch aux := 1; v.(type) {
case nil:
    fmt.Println("nil")
case T1:
    fmt.Println("T1", aux)
case T2:
    fmt.Println("T2", aux)
}
```

## Type assertions

A type assertion provides access to an interface value's underlying concrete value.

```go
t := i.(T)
```

This statement asserts that the interface value `i` holds the concrete type `T` and assigns the underlying `T` value to the variable `t`. If `i` does not hold a `T`, the statement will trigger a panic. `i` must always evaluate to interface type otherwise it’s a compile-time error.

* If `T` from `i.(T)` is an interface type then such assertion checks if dynamic type of `i` implements interface `T`.
* If `T` from `i.(T)` is not an interface type then such assertion checks if dynamic type of `v` is identical to `T`.

`T` is either a type identifier or type literal like:

```go
type I interface {
    walk()
    quack()
}
type S struct{}

func (s S) walk() {}
func (s S) quack() {}

var i I
i = S{}
j := i.(interface {
    walk()
})
```

To test whether an interface value holds a specific type, a type assertion can return two values: the underlying value and a boolean value that reports whether the assertion succeeded.

```go
t, ok := i.(T)
```

If `i` holds a `T`, then `t` will be the underlying value and `ok` will be `true`. If not, `ok` will be `false` and `t` will be the zero value of type `T`, and no panic occurs.

Missing the optional result value in a type assertion will make current goroutine panic if the type assertion fails.

```go
var v interface{} = "abc"
_, ok = v.(int) // will not panic
_ = v.(int)     // will panic!
```

## Identical types

> [Identical types in Go](https://medium.com/golangspec/identical-types-in-go-9cb89b91fe25)

Each type in Go has something which is called an underlying type.

* Universe block contains some predeclared identifiers binding to types like boolean, string or numeric.
* For each predeclared `type T` its underlaying type is `T` (no trap here).

```go
type X string  // underlying type of X is string
type Y X       // underlying type of Y is string
type Z [10]int // underlying type of Z is [10]int
```

* Named types are new ones specified using `type` name, optionally prefixed with package name. Package name is used to access name exported from other package (preceded with proper import statement).
* Unnamed types use type literals while referring to themselves like f.ex.:

```go
map[string]int
chan<- int
[]float32
```

Two types in Go are identical or different:

1. Two named types are identical if both are created through the same `type` declaration.

```go
type (
    T1 string
    T2 string
)
```

1. named and unnamed types are different (no exceptions),
2. unnamed types are identical if corresponding type literals are the same.

## Assignability

`T` is an interface type and `x` implements `T`:

```go
type I1 interface {
    M1()
}

type I2 interface {
    M1()
}

type T struct{}

func (T) M1() {}

func main() {
    var v1 I1 = T{}
    var v2 I2 = v1
    _ = v2
}
```

It doesn’t matter how these types are structured:

```go
type I1 interface {
    M1()
    M2()
}
type I2 interface {
    M1()
    I3
}
type I3 interface {
    M2()
}
type T struct{}
func (T) M1() {}
func (T) M2() {}
func main() {
    var v1 I1 = T{}
    var v2 I2 = v1
    _ = v2
}
```

Methods sets don’t have to be equal:

```go
type I1 interface {
    M1()
    M2()
}
type I2 interface {
    M1()
}
type T struct{}
func (T) M1() {}
func (T) M2() {}
func main() {
    var v1 I1 = T{}
    var v2 I2 = v1
    _ = v2
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://strctr.gitbook.io/programming/01-languages/go-lang/02-language/08-type-system.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
