Шпаргалка по Go: slices, maps, channels
This is the reference manual for the Go programming language.
The pre-Go1.18 version, without generics, can be found
here.
For more information and other documents, see golang.org.
Go is a general-purpose language designed with systems programming
in mind. It is strongly typed and garbage-collected and has explicit
support for concurrent programming. Programs are constructed from
packages, whose properties allow efficient management of
dependencies.
The syntax is compact and simple to parse, allowing for easy analysis
by automatic tools such as integrated development environments.
Notation
The syntax is specified using a
variant
of Extended Backus-Naur Form (EBNF):
Syntax = { Production } .
Production = production_name «=» [ Expression ] «.» .
Expression = Term { «|» Term } .
Term = Factor { Factor } .
Factor = production_name | token [ «…» token ] | Group | Option | Repetition .
Group = «(» Expression «)» .
Option = «[» Expression «]» .
Repetition = «{» Expression «}» .
Productions are expressions constructed from terms and the following
operators, in increasing precedence:
| alternation
() grouping
[] option (0 or 1 times)
{} repetition (0 to n times)
Lowercase production names are used to identify lexical (terminal) tokens.
Non-terminals are in CamelCase. Lexical tokens are enclosed in
double quotes «» or back quotes «.
The form a … b represents the set of characters from
a through b as alternatives. The horizontal
ellipsis … is also used elsewhere in the spec to informally denote various
enumerations or code snippets that are not further specified. The character …
(as opposed to the three characters …) is not a token of the Go
language.
Source code representation
Source code is Unicode text encoded in
UTF-8. The text is not
canonicalized, so a single accented code point is distinct from the
same character constructed from combining an accent and a letter;
those are treated as two code points. For simplicity, this document
will use the unqualified term character to refer to a Unicode code point
in the source text.
Each code point is distinct; for instance, uppercase and lowercase letters
are different characters.
Implementation restriction: For compatibility with other tools, a
compiler may disallow the NUL character (U+0000) in the source text.
Implementation restriction: For compatibility with other tools, a
compiler may ignore a UTF-8-encoded byte order mark
(U+FEFF) if it is the first Unicode code point in the source text.
A byte order mark may be disallowed anywhere else in the source.
Characters
The following terms are used to denote specific Unicode character categories:
newline = /* the Unicode code point U+000A */ .
unicode_char = /* an arbitrary Unicode code point except newline */ .
unicode_letter = /* a Unicode code point categorized as «Letter» */ .
unicode_digit = /* a Unicode code point categorized as «Number, decimal digit» */ .
In The Unicode Standard 8.0,
Section 4.5 «General Category» defines a set of character categories.
Go treats all characters in any of the Letter categories Lu, Ll, Lt, Lm, or Lo
as Unicode letters, and those in the Number category Nd as Unicode digits.
Letters and digits
The underscore character _ (U+005F) is considered a lowercase letter.
letter = unicode_letter | «_» .
decimal_digit = «0» … «9» .
binary_digit = «0» | «1» .
octal_digit = «0» … «7» .
hex_digit = «0» … «9» | «A» … «F» | «a» … «f» .
Lexical elements
Comments serve as program documentation. There are two forms:
-
Line comments start with the character sequence //
and stop at the end of the line. -
General comments start with the character sequence /*
and stop with the first subsequent character sequence */.
Arrays, Slices and Maps in Go
Three new books, Go Optimizations 101,
Go Details & Tips 101
and Go Generics 101
are published now.
It is most cost-effective to buy all of them through this book bundle
in the Leanpub book store.
Strictly speaking, there are three kinds of first-class citizen container types in Go, array, slice and map. Sometimes, strings and channels can also be viewed as container types, but this article will not touch the two kinds of types. All container types talked about in the current article are arrays, slices and maps.
There are many container related details in Go. This article will list them one by one.
Simple Overview of Container Types and Values
Each value of the three kinds of types is used to store a collection of element values. The types of all the elements stored in a container value are identical. The identical type is called the element type of (the container type of) the container value.
Each element in a container has an associated key. An element value can be accessed or modified through its associated key. The key types of map types must be comparable types.
The key types of array and slice types are all the built-in type int. The keys of the elements of an array or slice are non-negative integers which mark the positions of these elements in the array or slice.
The non-negative integer keys are often called indexes.
Each container value has a length property, which indicates how many elements are stored in that container. The valid range of the integer keys of an array or slice value is from zero (inclusive) to the length (exclusive) of the array or slice. For each value of a map type, the key values of that map value can be an arbitrary value of the key type of the map type.
There are also many differences between the three kinds of container types. Most of the differences originate from the differences between the value memory layouts of the three kinds of types.
From the last article, value parts, we learned that an array value consists of only one direct part, however a slice or map value may have an underlying part, which is referenced by the direct part of the slice or map value.
Elements of an array or a slice are both stored contiguously in a continuous memory segment. For an array, the continuous memory segment hosts the direct part of the array. For a slice, the continuous memory segment hosts the underlying indirect part of the slice. The map implementation of the standard Go compiler/runtime adopts the hashtable algorithm.
So all elements of a map are also stored in an underlying continuous memory segment, but they may be not contiguous. There may be many holes (gaps) within the continuous memory segment. Another common map implementation algorithm is the binary tree algorithm.
Whatever algorithm is used, the keys associated with the elements of a map are also stored in (the underlying parts of) the map.
We can access an element through its key. The time complexities of element accesses on all container values are all O(1), though, generally map element accesses are several times slower than array and slice element accesses. But maps have two advantages over arrays and slices:
- the key types of maps can be any comparable types.
- maps consume much less memory than arrays and slices if most elements are zero values.
From the last article, we have learned that the underlying parts of a value will not get copied when the value is copied. In other words, if a value has underlying parts, a copy of the value will share the underlying parts with the value. This is the root reason of many behavior differences between array and slice/map values. These behavior differences will be introduced below.
Literal Representations of Unnamed Container Types
The literal representations of the three kinds of unnamed container types:
- array types: [N]T
- slice types: []T
- map types: map[K]T
where
- T is an arbitrary type. It specifies the element type of a container type. Only values of the specified element type can be stored as element values of values of the container type.
- N must be a non-negative integer constant. It specifies the number of elements stored in any value of an array type, and it can be called the length of the array type. This means the length of an array type is the inherent part of the array type. For example, [5]int and [8]int are two distinct array types.
- K is an arbitrary comparable type. It specifies the key type of a map type. Most types in Go are comparable, incomparable types are listed here.
Here are some container type literal representation examples: const Size = 32
type Person struct {
name string
age int
}
/* Array types */
[5]string
[Size]int
// Element type is a slice type: []byte
[16][]byte
// Element type is a struct type: Person
[100]Person
/* Slice types *
[]bool
[]int64
// Element type is a map type: map[int]bool
[]map[int]bool
// Element type is a pointer type: *int
[]*int
/* Map types */
map[string]int
map[int]bool
// Element type is an array type: [6]string
map[int16][6]string
// Element type is a slice type: []string
map[bool][]string
// Element type is a pointer type: *int8,
// and key type is a struct type.
map[struct{x int}]*int8
The sizes of all slice types are the same. The sizes of all map types are also the same. The size of an array type depends on its length and the size of its element type. The size of a zero-length array type or an array type with a zero-size element type is zero.
Container Value Literals
Go programming language Cheatsheet
Go language is an open-source, statically typed programming language by Google. Go is highly recommended for creation of highly scalable and available web applications.
Basics
Sample Go program
package main
import «fmt»
func main() {
fmt.Printf(«Go Hello, World!»)
}
- package — package declaration which is mandatory for all Go programs.
- import «fmt» — used to import built-in fmt package.
- func main() — function is the starting point of execution of any Go program.
- fmt.printf — inbuilt library function which is used to print the given message.
- fmt.Scanf — inbuilt library function which is used to read the data.
- // — to comment a single line
- /**/ — to comment multiple lines
Variables
Declaring Variables
var variable-name data-type
Assigning value to variables
variable-name := value // It's not mandatory to declare a variable when you use this.
Example
var i int // declaring int variable at functional level
j := 99 // short assignment without var declaration
Data types
1. Numeric Data types
Integer Data types
uint8 | 8-bit unsigned integer | 1 byte | 0 to 255 | byte |
int8 | 8-bit signed integer | 1 byte | -128 to 127 | N/A |
int16 | 16-bit signed integer | 2 bytes | -32768 to 32767 | N/A |
unit16 | 16-bit unsigned integer | 2 bytes | 0 to 65,535 | N/A |
int32 | 32-bit signed integer | 4 bytes | -2,147,483,648 to 2,147,483,647 | int (on 32 bit systems), rune |
uint32 | 32-bit unsigned integer | 4 bytes | 0 to 4,294,967,295 | uint (on 32 bit systems) |
int64 | 64-bit signed integer | 8 bytes | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | int (on 64 bit systems) |
uint64 | 64-bit unsigned integer | 8 bytes | 0 to 18,446,744,073,709,551,615 | uint (on 64 bit systems) |
Float Data types
float32 | 32-bit signed floating point number | 4 bytes | N/A |
float64 | 64-bit signed floating point number | 8 bytes | float |
complex64 | Number has float32 real and imaginary parts | 8 bytes | N/A |
complex128 | Number has float64 real and imaginary parts | 16 bytes | N/A |
2. Boolean Data types
bool | Stores either true or false | 1 byte | True or false |
3. String Data types
string | Sequence of characters |
rune | Alias for uint32 but also represent single character in string |
Example
var (
integer uint32 = 4294967295
flt float64 = 3.14
complexNum complex128 = cmplx.Sqrt(8 — 6i)
str string = «Hello World»
rne rune = 'A'
boolean bool = true
)
Operators
Arithmetic Operator | + , — , * , / , % |
comparision Operator | < , > , =, != , == |
Bitwise Operator | & , ^ , |, &^, |
Logical Operator | && , ||, ! |
Assignment Operator | = , += , -= , *= , /= , %=, =, &=, ^=, |= |
String Functions
len(str) | to return the length of string str |
strings.Compare(a, b) | Compares two strings a and b lexically and returns 0 if the both strings are equal and 1 if string 1 is greater than string 2 and -1 if string 1 is less than string 2. |
strings.Contains(str, substr) | returns true if substring is found in the string str |
strings.ToUpper(str) | to change the str to Upper Case |
strings.ToLower(str) | to change the str to lower Case |
strings.HasPrefix(str,»prefix») | returns true if the string str is starting with a prefix |
strings.HasSuffix(str,»suffix») | returns true if the string str is ending with a suffix |
strings.Index(str, substr) | searches for a particular text within a string and returns it's index. If not found then it will returns -1. |
strings.Join(stringSlice []string, sep string) | contanates the elements of an slice with seperators |
strings.Count(str, sep string) | returns the count of number of non-overlapping instances of a character/string/text in string |
Arrays
Syntax
var array-name[size] data-type; // declaration of an array
array-name := [size] data-type {value0,value1,…,value_size-1} // assigning values to array
Example
var fruits [3] string //Declaring a string array of size 3
fruits[0] = «Mango»
fruits[1] = «Apple»
fruits[2] = «Orange»
arr := […] int {1,2,3,4,5} //Declaring a integer array of size 5
Conditional Statements
1. If
if (conditional-expression) {
//code
}
2. If-else
if (conditional-expression) {
//code
} else {
//code
}
3. If-else-if ladder
if (conditional-expression-1) {
//code
} else if (conditional-expression-2) {
//code
} else if (conditional-expression-3) {
//code
}
….
else {
//code
}
4. Switch
switch (conditional-expression) {
case value1:
//code
break; //optional
case value2:
//code
break; //optional
…
default:
//code to be executed when all the above cases are not matched;
}
Loops
1. For
for (Initialization; Condition; Increment/decrement) {
//code
}
2. while
Go doesn't have while as a keyword, you can use for instead.
for (condition) {
//code
}
3. Do-While
Go doesn't have do…while you can improvised using for.
for (true) {
//code
if(!conditional-expression) {
break;
}
}
Functions
func function_name(parameters) return_type { // defining a function
//code
}
function_name(parameters) // calling a function
Pointers
var pointername *datatype;
Example
num := 10;
var ptr *int; // pointer variable
ptr = #
Structures
type structure_name struct { // defining a structure
member definition;
member definition;
…
member definition;
}
var structure-variable structure-name //declaring a structure variable
Slice
A slice can be formed from an array using a low and high bound indices which are separated by a colon:
array-name[lowIndex : highIndex]
- Slices never store any data
- Slices just describes the section of an array
- If you modify the elements present in a Slice, it's corresponding array elements also will see changes.
- If you omit high or low bounds while slicing, Go automatically use their defaults instead. The default values for low bound is zero and for the high bound is the length of the slice.
- Slice can be created by using built-in function make. The below creates a slice [0 0 0 0 0]
Maps
Maps is an unordered collection of key and its value.
var map-name map[key-data-type]value-data-type //declaring a map
map-name = make(map[key-data-type]value-data-type) //defining a map
Concurrency
Go supports concurrent execution of tasks using Goroutines and Channels.
Go-routines
Advantage of Go-routines is that the control will not wait for the execution of function to complete. It will just call the function and continue to execute the rest of the code. Function execution will happen in concurrent with the rest of code execution.
go function-name([arguments])
Channels
Синтаксис Go — Русские Блоги
Базовая структура:var имя переменной тип переменной = значение Примечание._(Подчеркивание) — это специальное имя переменной, любое присвоенное ему значение будет отброшено
package main
/ * Глобальные переменные * /
// Необходимо только объявление, var и тип переменной
var a int
var b, c int
// Объявление и инициализация, тип переменной можно не указывать
var d int = 1
var e, f int = 1, 2
var g = 1 // автоматически определяем тип
var h, i = 1, «string» // тип может быть другим
func main() {
/ * Специальное объявление локальных переменных * /
j := 1;
k, l := 1, 2
}
постоянный
Константы можно определять как числовые, логические или строковые типы.
/ * Глобальное и локальное объявления совпадают * /
const a int = 1
const b = 1
const c, d = 1, 2 «строка» // Тип может быть другим
Встроенные базовые типы
Boolean
Тип логического значения — bool, значение true или false, а значение по умолчанию — false.
Примечание: 0 и ненулевое значение не могут использоваться для представления истины или ложи.
Числовой тип
1. Целое число
* Разделены на беззнаковые и подписанные, например: int и uint
* 8, 16, 32, 64 бит, например: int32 и uint32
* Rune — это другое имя для int32, byte — это другое имя для uint8
2. Типы с плавающей точкой float32 и float64
3. Комплексные числа complex64 и complex128
Примечание: нельзя выполнять операции между разными типами.
Строка
определение
var a string
var b string = «»
func test() {
no, yes, maybe := «no», «yes»
}
модифицировать
s := «hello»
c: = [] byte (s) // преобразовываем строку s в [] байтовый тип
c[0] = 'c'
s2: = string (c) // преобразовать обратно в строковый тип
fmt.Printf(«%s
«, s2)
связь
s := «hello,»
m := » world»
a := s + m
fmt.Printf(«%s
«, a)
Исходный формат вывода
Строка, заключенная в `- это необработанная строка, то есть форма строки в коде — это форма, когда она печатается, она не имеет escape-символа, и новая строка будет выводиться как есть.
m := `hello
world`
Тип ошибки
Go имеет встроенный тип ошибок, который специально используется для обработки сообщений об ошибках. В пакете Go также есть специальный пакет ошибок для обработки ошибок:
err := errors.New(«emit macho dwarf: elf header corrupted»)
if err != nil {
fmt.Print(err)
}
Базовое хранилище данных Go
Изображение ниже взято из статьи о структуре данных Go в блоге Russ Cox. Вы можете видеть, что всем этим базовым типам выделяется блок памяти на нижнем уровне, а затем сохраняется соответствующее значение.
Некоторые хитрости
Заявление группы
import(
«fmt»
«os»
)
const(
i = 100
pi = 3.1415
prefix = «Go_»
)
var(
i int
pi float32
prefix string
)
йота перечисление
В Go есть ключевое слово iota, которое используется при объявлении enum. Его начальное значение по умолчанию — 0, и оно увеличивается на 1 для каждого вызова:
const(
x = iota // x == 0
y = iota // y == 1
z = iota // z == 2
w // Когда объявление константы опускает значение, значение по умолчанию буквально совпадает с предыдущим значением. Неявно сказано, что w = iota, поэтому w == 3. Фактически, указанные выше y и z также могут использоваться без «= iota».
)
const v = iota // каждый раз, когда встречается ключевое слово const, iota будет сброшена, в это время v == 0
const (
e, f, g = iota, iota, iota // e = 0, f = 0, g = 0 iota имеет то же значение в той же строке
)
Если явно не заданы другие значения или йота, для первой константы каждой группы констант по умолчанию устанавливается значение 0, а для второй и последующих констант по умолчанию устанавливается значение предыдущей константы, если предыдущая константа Значение — йота, также установлено йота.
Частные и публичные
Переменные или функции, начинающиеся с заглавных букв, являются общедоступными, что эквивалентно общедоступным в java. Переменные или функции, начинающиеся со строчных букв, являются частными, что эквивалентно частным в java.
array、slice、map
array
Базовая структура:var имя переменной [длина] тип
- Длину массива нельзя изменить
- Длина также является частью типа массива, поэтому3int и [4] int — разные типы
- При передаче массива в качестве параметра функции передается фактически копия массива, а не его указатель.
var arr [10] int // объявляет массив типа int
a: = [3] int {1, 2, 3} // объявляет массив int длиной 3
b: = [10] int {1, 2, 3} // Объявляет массив int длиной 10, где первые три элемента инициализируются значениями 1, 2, 3, а остальные по умолчанию — 0
c: = […] int {4, 5, 6} // Вы можете опустить длину и использовать метод `…`. Go автоматически вычислит длину на основе количества элементов
slice
заявление
Базовая структура:var имя переменной [] тип
Slice — это ссылочный тип. Срез всегда указывает на базовый массив, и объявление среза также может быть похоже на массив, но не требует длины.
var slice []int
var slice = []int{1, 2, 3}
slice := []byte {'a', 'b', 'c'}
array := [3]byte {'a', 'b', 'c'}
slice: = array [1, 2] // Срез получается массивом [i: j], где i — начальная позиция массива, j — конечная позиция, но не включает array [j], а его длина j-i.
Встроенная функция
- len получает длину среза
- cap Получите максимальную вместимость среза
- append добавляет один или несколько элементов в срез, а затем возвращает срез того же типа, что и срез
- Функция копирования copy копирует элементы из src исходного слайса в целевой dst и возвращает количество скопированных элементов.
Длина и вместимость
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
s := a[0:]
s = append(s, 11, 22, 33)
sa := a[2:7]
sb := sa[3:5]
fmt.Println (a, len (a), cap (a)) // Вывод: [1 2 3 4 5 6 7 8 9 0] 10 10
fmt.Println (s, len (s), cap (s)) // Вывод: [1 2 3 4 5 6 7 8 9 0 11 22 33] 13 20
fmt.Println (sa, len (sa), cap (sa)) // Вывод: [3 4 5 6 7] 5 8
fmt.
Println (sb, len (sb), cap (sb)) // Вывод: [6 7] 2 5
- Длина — это сохраненное число, а емкость — это сохраненное число.
- Для массивов длина и емкость всегда равны
- Емкость среза может быть больше, чем длина. Если емкости недостаточно, новое пространство массива будет выделяться динамически.
- array [i: j: k], k-i — емкость, k по умолчанию — длина массива
ловушка
Когда емкость среза все еще свободна, добавленные элементы будут напрямую использовать свободное пространство емкости, но как только количество добавленных элементов превысит исходное заданное значение емкости, диспетчер памяти повторно откроет большую память Пространство используется для хранения дополнительных элементов, а исходные элементы будут скопированы и помещены в это вновь открытое пространство памяти.
a := []int{1, 2, 3, 4}
sa := a[1:3]
fmt.Printf («% p n», sa) // Вывод: 0xc0840046e0
sa = append(sa, 11, 22, 33)
fmt.Printf («% p n», sa) // Вывод: 0xc084003200
Ссылка на ссылку
map
заявление
Базовая структура:map[keyType]valueType
m1 := make(map[string]string)
m1 [«aa»] = «bb» // добавить
m2 := map[string]float32{«C»:5, «Go»:4.5, «Python»:4.5, «C++»:2 }
m2 [«C ++»] = 5 // изменить
delete (m2, «C») // удаляем элемент с ключом C
len (m2) // длина
Характеристики
- Карта неупорядочена, и распечатанная карта каждый раз будет отличаться. Ее нельзя получить по индексу, но необходимо получить по ключу
- Длина карты не фиксирована, то есть, как и срез, это также ссылочный тип.
- Карта отличается от других базовых типов. Она не является потокобезопасной. При доступе к нескольким подпрограммам необходимо использовать механизм блокировки мьютекса.
- map [key], есть два возвращаемых значения: первое — значение, а второе — есть ли соответствующее значение.
сделать, новая операция
make используется для выделения памяти встроенных типов (map, slice и channel). new используется для различных типов распределения памяти.
- new возвращает указатель
- make возвращает инициализированное (ненулевое) значение
Нулевое значение
Что касается «нулевого значения», это не нулевое значение, а значение по умолчанию «до заполнения переменной», обычно 0. Вот несколько типов «нулевых значений»
int 0
int8 0
int32 0
int64 0
uint 0x0
rune 0 // Настоящий тип руны — int32
byte 0x0 // Фактический тип байта — uint8
float32 0 // Длина 4 байта
float64 0 // Длина 8 байт
bool false
string «»
Процесс и функция
Контроль процесса
Управление процессом в Go делится на три категории: условная оценка, управление циклом и безусловный переход.
if
Одним из мощных аспектов if в Go является то, что переменная может быть объявлена в условном выражении суждения. Область действия этой переменной может находиться только в блоке условной логики, а другие места не будут работать, как показано ниже.
// Вычислить значение x, а затем определить, больше ли оно 10 в соответствии с размером, возвращаемым x.
if x := computedValue(); x > 10 {
fmt.Println(«x is greater than 10»)
} else {
fmt.Println(«x is less than 10»)
}
// Если это место называется так, произойдет ошибка компиляции, потому что x — это переменная в условии
fmt.Println(x)
goto
В Go есть оператор goto — используйте его с умом. Используйте goto для перехода к метке, которая должна быть определена в текущей функции (с учетом регистра). Например, предположим такой цикл:
func myFunc() {
i := 0
Здесь: // Первое слово в этой строке, заканчивающееся двоеточием в качестве метки
println(i)
i++
goto Here // Перейти сюда
}
for
Одна из самых мощных управляющих логик в Go — for, которая может использоваться для чтения данных в цикле, а также может использоваться как while для управления логикой и итерационными операциями. Его синтаксис следующий:
/ * выражение1 и выражение3 — объявления переменных или возвращаемые значения вызова функции, выражение2 используется для условной оценки, выражение1 вызывается перед началом цикла, а выражение3 вызывается в конце каждого цикла. * /
for expression1; expression2; expression3 {
//…
}
/ * Например * /
for index:= 0; index < 10 ; index++ {
}
// Параллельное присваивание
for a, b:= 0, 0; b < 10 ; b++ {
}
пока заявление
/ * оператор while * /
for ; sum < 1000; {
sum += sum
}
//; можно не указывать
for sum < 1000 {
sum += sum
}
сломать и продолжить
/ * break и continue соответствуют java * /
for index := 10; index>0; index— {
if index == 5{
break // или продолжить
}
fmt.Println(index)
}
// перерыв выводит 10, 9, 8, 7, 6
// продолжить выводит 10, 9, 8, 7, 6, 4, 3, 2, 1
range
/ * For with range может использоваться для чтения данных среза и отображения * /
// Первое возвращаемое значение — ключ, второе возвращаемое значение — значение
// Если это срез, то ключ — это нижний индекс
for k,v:=range map {
fmt.Println(«map's key:»,k)
fmt.Println(«map's val:»,v)
}
switch
Разница с java в том, что каждыйcaseВыскочит после казниоператор переключения, Если вы хотите перейти к следующемуcase, Нужно присоединитьсяfallthroughКлючевое слово
i := 10
switch i {
case 1:
fmt.Println(«i is equal to 1»)
case 2, 3, 4:
fmt.Println(«i is equal to 2, 3 or 4»)
case 10:
fmt.Println(«i is equal to 10»)
fallthrough
default:
fmt.Println(«————————«)
}
функция
/* Базовая структура */
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
// Вот код логики обработки
// Возвращаем несколько значений
return output1, output2
}
/ * output1 и output2 можно не указывать, но необходимо указать тип возвращаемого значения * /
func funcName(input1 type1, input2 type2) (type1, type2) {
return «», «»
}
/ * Если есть только одно возвращаемое значение и переменная возвращаемого значения не объявлена, вы можете написать так * /
func funcName(input1 type1, input2 type2) type {
}
/ * Нет возвращаемого значения, все можно пропустить * /
func funcName(input1 type1, input2 type2) {
}
/ * Типы параметров до и после одинаковы, предыдущие типы параметров можно не указывать * /
func funcName(input1, input2 int, input3 string, input4, input5 float32) (output1, output2 int){
}
/ * Официальное предложение: лучше назвать возвращаемое значение, потому что возвращаемое значение не имеет имени, хотя код более лаконичен, но сгенерированный документ будет менее читабельным. * /
func SumAndProduct(A, B int) (add int, Multiplied int) {
add = A+B
Multiplied = A*B
// Это тоже особое место
return
}
Переменные параметры
Базовая структура:func myfunc(arg …int) {}
- Типы параметров все int
- Переменная arg — это часть int
for _, n := range arg {
fmt.Printf(«And the number is: %d
«, n)
}
Значение передачи и указатель
- Все переданные параметры функции копируются
- Даже если указатель передан, это копия указателя
Преимущества указателей
- Передача указателей позволяет нескольким функциям работать с одним и тем же объектом.
- Передача указателей относительно легкая (8 байт), просто передайте адрес памяти, мы можем использовать указатели для передачи больших структур. Если он передается по значению параметра, на каждую копию будет потрачено относительно больше служебных данных (памяти и времени). Поэтому, когда вы хотите передать большие структуры, использование указателей — разумный выбор.
- Механизмы реализации трех типов строки, фрагмента и карты в Go похожи на указатели, поэтому их можно передавать напрямую, а не передавать указатель после получения адреса. (Примечание: если функции необходимо изменить длину фрагмента, ей все равно необходимо принять адрес и передать указатель)
package main
import «fmt»
// Простая функция, реализующая работу параметра +1
func add1 (a * int) int {// Обратите внимание,
* a = * a + 1 // изменить значение a
return * a // вернуть новое значение
}
func main() {
x := 3
fmt.Println («x =», x) // должно вывести «x = 3»
x1: = add1 (& x) // вызываем add1 (& x) для передачи адреса x
fmt.Println («x + 1 =», x1) // должно вывести «x + 1 = 4»
fmt.Println («x =», x) // должно вывести «x = 4»
}
defer
- Оператор defer будет выполнен до того, как функция вернется
func ReadWrite() bool {
file.Open(«file»)
defer file.
Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}
- Defer использует режим «последний пришел — первый ушел», поэтому следующий код выведет 4 3 2 1 0
for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i) }
Функция как значение, тип
Базовая структура:type typeName func(input1 inputType1 , input2 inputType2) (result1 resultType1)
- Функции с одним и тем же списком параметров и списком возвращаемых значений относятся к одному и тому же типу функции. Например, функции isOdd и isEven в следующем примере.
package main
import «fmt»
type testInt func (int) bool // объявляет тип функции
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
// Объявленный тип функции используется в этом месте как параметр
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println(«slice = «, slice)
odd: = filter (slice, isOdd) // функция передается как значение
fmt.Println(«Odd elements of slice are: «, odd)
even: = filter (slice, isEven) // функция передается как значение
fmt.Println(«Even elements of slice are: «, even)
}
Golang Make Function — Initializing Slices, Maps, and Channels (Size and Capacity)
- Go has a special make function that can be used to initialize channels, slices, and maps.
- Using make, we can specify the memory and capacity constraints of the data type being created, giving us low-level control that’s not available to us using regular constructor functions
Basic Usage#
make is a special function in Go that can take a different number of types and arguments.
It returns an instance of the type passed as the first argument:
obj := make(someType, optionalArgument1, optionalArgument2)
Here, someType can be a slice, a map, or a channel.
Creating Slices#
We can initialize slices of any type using make:
words := make([]string, 2)
- Here, the first argument is the type and the second argument is the length.
- By default, a new slice is initialized and filled with as many empty values as the length specified.
- So, in this case, the value of words would be []string{«», «»}
We can also pass a third argument when creating a slice, which is the capacity. The capacity denotes how much memory is allocated to a slice, even though its length may not be as much.
For example, if we create words using capacity as well:
words := make([]string, 2, 5)
the value of words now is still []string{«», «»}, but the underlying memory is allocated for 5 string values.
So, if we add another element using append, Go doesn’t allocate any more memory under the hood:
words = append(words, «lorem ipsum»)
By default, if you don’t assign any capacity, Go assumes a default capacity. When appending more items, Go provisions more capacity as and when needed.
The capacity argument has to be greater than the length argument, otherwise the code will not build
So, specifying the capacity is very useful if you know how big your slice will be beforehand, since we can skip the extra allocation each time the default capacity is exceeded.
Note that specifying the capacity doesn’t cap the max limit, but rather provisions the initial capacity that needs to be re-allocated when more elements are added
Creating Maps#
Using make with maps is not as straightforward as with slices.
// make an empty map
m := make(map[int]string)
// make a map with optional capacity for `n` elements
m := make(map[int]string, n)
- We can still make empty map instances, and specify the capacity, but the capacity here is taken by the language as a hint, and doesn’t make any guarantees about the exact capacity allocated.
- The language specification itself mentions that the second argument describes “initial space for approximately n elements”
Creating Channels#
We can create different types of channels using make:
- Unbuffered channels, that cannot store any data, and only act as a data pipe:// create an unbuffered channel of integers
out := make(chan int) - Buffered channels which can store some quantity of data// create a buffered channel, that can hold a maximum of 3 integer values
out := make(chan int, 3)
Make vs New#
- Go also has a built in function called new, which often appears in similar scenarios as make, but has different functionality.
- While make is able to allocate variable memory and returns an instance of the provided type, new can only initialize empty instances, and returns a pointer of the provided argument.
- Let’s take a look at an example:
// returns a slice with 1 default (0) int element s1 := make([]int, 1)
// returns a pointer to an empty slice
s2 := new([]int)
fmt.Println(s1)
fmt.Println(s2)
If we run this code, we will get:
[0]
&[]
Conclusion#
- make is a versatile built-in function that can be used to initialize different data types.
- The arguments and their significance depends on the type of variable being initialized.
- You can read more about the details of how make and new work in the language specification.
Way to Go Part 3: The Go Standard Library
This article is the third in a multi-part series on the Go programming language. It provides details on the Go Standard Library.
The first article in the series provides an overview of the language and a quick-start guide. The second installment provides details on all the syntax in the Go language.
Future articles will cover concurrency, reflection, solutions to common tasks, modules, testing, and the future of Go.
Builtins
The builtin constants, variables, types, and functions provided by Go are listed as being in the standard library package «builtin» for documentation purposes, but no such package actually exists.
The following sections describe each of the provided builtins.
Builtin Constants
The provided constants include the boolean literals true and false, and iota.
iota is not actually a constant. It is a global counter that is set to zero at the beginning of every const definition, which is the only place it can be used.
The value of iota is incremented by one after each line in the const definition, except for blank lines and comment lines. It is typically used to define enumerated values.
The last expression involving iota is repeated for subsequent constant values but uses an incremented value of iota.
For example:
const (
red = iota // 0
green // 1
blue // 2
)
const (
north = iota + 1 // iota = 0, 0 + 1 = 1
south // iota = 1, 1 + 1 = 2
east // iota = 2, 2 + 1 = 3
west // iota = 3, 3 + 1 = 4
)
const (
t1 = iota * 3 // iota = 0, 0 * 3 = 0
t2 // iota = 1, 1 * 3 = 3
t3 // iota = 2, 2 * 3 = 6
)
const (
_ = iota // iota = 0, ignore first value
kb int64 = 1 ' 4.»))
// It's "true" that 5 is '>' 4.
}
Unicode
The standard library package unicode defines many constants, variables, functions, and types for testing and converting rune values.
Many of the constants define ranges of Unicode characters used in specific written languages.
Many of the functions take a rune and return a bool, indicating whether the rune is a member of a particular category of Unicode characters. These include IsDigit, IsLetter, IsLower, IsUpper, and IsSpace.
IsSpace determines whether a rune represents a whitespace character. These include the characters space, tab, newline, carriage return, and the less commonly used characters formfeed, non-breaking space (NBSP), vertical tab, and next line (NEL).
The functions ToLower and ToUpper take a rune and return another rune.