From 809452dc96752b5c807368034618742a4253c15a Mon Sep 17 00:00:00 2001 From: Slava Zgordan Date: Fri, 14 Aug 2015 11:15:50 +0200 Subject: [PATCH] 02.3 --- ru/02.2.md | 2 +- ru/02.3.md | 299 +++++++++++++++++++++++++------------------------- ru/preface.md | 2 +- 3 files changed, 153 insertions(+), 150 deletions(-) diff --git a/ru/02.2.md b/ru/02.2.md index 3edafbb..0b01dbb 100644 --- a/ru/02.2.md +++ b/ru/02.2.md @@ -451,4 +451,4 @@ - [Содержание](preface.md) - Предыдущий раздел: ["Hello, Go"](02.1.md) -- Следующий раздел: [Условные операторы и функции](02.3.md) +- Следующий раздел: [Управляющие конструкции и функции](02.3.md) diff --git a/ru/02.3.md b/ru/02.3.md index 4098c80..9db4d4f 100644 --- a/ru/02.3.md +++ b/ru/02.3.md @@ -1,70 +1,71 @@ -# 2.3 Условные операторы и функции +# 2.3 Управляющие конструкции и функции -В этом разделе мы поговорим об условных операциях и функциях в Go. +В этом разделе мы поговорим об управляющих конструкциях и функциях в Go. -## Control statement +## Управляющие конструкции -The greatest invention in programming is flow control. Because of them, you are able to use simple control statements that can be used to represent complex logic. There are three categories of flow control: conditional, cycle control and unconditional jump. +Контроль потока команд является величайшим изобретением в области программирования. Благодаря этому Вы можете использовать управляющие конструкции, чтобы реализовать сложную логику в своих приложениях. Существуют три категории контроля потока команд: условные конструкции, циклы и безусловные переходы. ### if -`if` will most likely be the most common keyword in your programs. If it meets the conditions, then it does something and it does something else if not. +`if`, вероятно, будет часто встречающимся ключевым словом в Ваших программах. Если условие, указанное в нем, удовлетворяется, выполняется блок кода, а если не удовлетворяется, то выполняется что-то другое. -`if` doesn't need parentheses in Go. +В Go `if` не нуждается в скобках. if x > 10 { - fmt.Println("x is greater than 10") + fmt.Println("x больше 10") } else { - fmt.Println("x is less than or equal to 10") + fmt.Println("x меньше или равно 10") } -The most useful thing concerning `if` in Go is that it can have one initialization statement before the conditional statement. The scope of the variables defined in this initialization statement are only available inside the block of the defining `if`. +Наиболее полезной чертой `if` в Go является то, что перед выражением условия может находиться выражение присваивания. Область видимости переменных, инициализированных в этом выражении, ограничена блоком, относящимся к `if`: - // initialize x, then check if x greater than + // определяем x, затем проверяем, больше ли x, чем 10 if x := computedValue(); x > 10 { - fmt.Println("x is greater than 10") + fmt.Println("x больше 10") } else { - fmt.Println("x is less than 10") + fmt.Println("x меньше или равно 10") } - // the following code will not compile + // А этот код не скомпилируется fmt.Println(x) -Use if-else for multiple conditions. +Для множественных условий используйте if-else: if integer == 3 { - fmt.Println("The integer is equal to 3") + fmt.Println("Целое число равно 3") } else if integer < 3 { - fmt.Println("The integer is less than 3") + fmt.Println("Целое число меньше 3") } else { - fmt.Println("The integer is greater than 3") + fmt.Println("Целое число больше 3") } ### goto -Go has a `goto` keyword, but be careful when you use it. `goto` reroutes the control flow to a previously defined `label` within the body of same code block. +Go has a `goto` keyword, but be careful when you use it. `goto` reroutes the contro to a previously defined `label` within the body of same code block. +В Go есть ключевое слово `goto`, но, исопльзуя его, будьте осторожными. `goto` перенаправляет управление потоком команд к заранее определенной `метке` внутри блока кода, в котором оно находится. func myFunc() { i := 0 - Here: // label ends with ":" + Here: // Метка заканчивается на ":" fmt.Println(i) i++ - goto Here // jump to label "Here" + goto Here // Переходим к метке "Here" } -The label name is case sensitive. +Названия меток чувствительны к регистру. ### for -`for` is the most powerful control logic in Go. It can read data in loops and iterative operations, just like `while`. +`for` - самый мощный способ управления потоком в Go. Он может работать с данными в циклах и итеративных операциях, так же, как `while`. for expression1; expression2; expression3 { //... } -`expression1`, `expression2` and `expression3` are all expressions, where `expression1` and `expression3` are variable definitions or return values from functions, and `expression2` is a conditional statement. `expression1` will be executed once before looping, and `expression3` will be executed after each loop. +`expression1`, `expression2` и `expression3` - это выражения, где `expression1` и `expression3` - определения переменных или значений, возвращаемых функциями, а `expression2` - условное выражение. `expression1` выполняется один раз перед запуском цикла, а `expression3` выполняется после каждого шага цикла. -Examples are more useful than words. +Примеры, однако, полезнее слов: package main import "fmt" @@ -74,80 +75,80 @@ Examples are more useful than words. for index:=0; index < 10 ; index++ { sum += index } - fmt.Println("sum is equal to ", sum) + fmt.Println("sum равно ", sum) } - // Print:sum is equal to 45 + // Print:sum равно 45 -Sometimes we need multiple assignments, but Go doesn't have the `,` operator, so we use parallel assignment like `i, j = i + 1, j - 1`. +Иногда нам могут понадобиться множественные присваивания, но в Go нет оператора `,`, поэтому можно использовать параллельное присваивание типа `i, j = i + 1, j - 1`. -We can omit `expression1` and `expression3` if they are not necessary. +Можно опускать `expression1` и `expression3`, если в них нет необходимости: sum := 1 for ; sum < 1000; { sum += sum } -Omit `;` as well. Feel familiar? Yes, it's identical to `while`. +Опускаем также `;`. Знакомо? Да, такая конструкция идентична `while`. sum := 1 for sum < 1000 { sum += sum } -There are two important operations in loops which are `break` and `continue`. `break` jumps out of the loop, and `continue` skips the current loop and starts the next one. If you have nested loops, use `break` along with labels. +В циклах есть две важные операции `break` и `continue`. `break` прекращает выполнение цикла, а `continue` прекращает выполнение текущей итерации цикла и начинает выполнять следующую. Если у Вас есть вложенные циклы, используйте `break` вместе с метками. for index := 10; index>0; index-- { if index == 5{ - break // or continue + break // или continue } fmt.Println(index) } - // break prints 10、9、8、7、6 - // continue prints 10、9、8、7、6、4、3、2、1 + // break печатает 10、9、8、7、6 + // continue печатает 10、9、8、7、6、4、3、2、1 -`for` can read data from `slice` and `map` when it is used together with `range`. +`for` может читать данные из `срезов` и `карт` при помощи ключевого слова `range`. for k,v:=range map { - fmt.Println("map's key:",k) - fmt.Println("map's val:",v) + fmt.Println("Ключ карты:",k) + fmt.Println("Значение карты:",v) } -Because Go supports multi-value returns and gives compile errors when you don't use values that were defined, you may want to use `_` to discard certain return values. +Так как в Go может возвращаться сразу несколько значений, а если не использовать какое-либо присвоенное значение, возвращается ошибка компиляции, можно использовать `_`, чтобы отбросить ненужные возвращаемые значения: for _, v := range map{ - fmt.Println("map's val:", v) + fmt.Println("Значение элемента карты:", v) } ### switch -Sometimes you may find that you are using too many `if-else` statements to implement some logic, which may make it difficult to read and maintain in the future. This is the perfect time to use the `switch` statement to solve this problem. +Иногда выходит так, что для того, чтобы реализовать какую-нибудь программную логику, приходится использовать слишком много выражений `if-else`, что приводит к том у, что код становится трудно читать и поддерживать в будущем. Самое время воспользоваться ключевым словом `switch`, чтобы решить эту проблему! switch sExpr { case expr1: - some instructions + какие-нибудь инструкции case expr2: - some other instructions + другие инструкции case expr3: - some other instructions + еще инструкции default: - other code + другой код } -The type of `sExpr`, `expr1`, `expr2`, and `expr3` must be the same. `switch` is very flexible. Conditions don't have to be constants and it executes from top to bottom until it matches conditions. If there is no statement after the keyword `switch`, then it matches `true`. +Тип `sExpr`, `expr1`, `expr2`, and `expr3` должен быть один и тот же. `switch` очень гибок. Условия не обязаны быть постоянными, условия проверяются сверху вниз, пока не будет достигнуто условие, которое удовлетворяется. Если после ключевого слова `switch` нет выражения, ищется `true`. i := 10 switch i { case 1: - fmt.Println("i is equal to 1") + fmt.Println("i равно 1") case 2, 3, 4: - fmt.Println("i is equal to 2, 3 or 4") + fmt.Println("i равно 2, 3 или 4") case 10: - fmt.Println("i is equal to 10") + fmt.Println("i равно 10") default: - fmt.Println("All I know is that i is an integer") + fmt.Println("Все, что я знаю - это то, что i - целое число") } -In the fifth line, we put many values in one `case`, and we don't need to add the `break` keyword at the end of `case`'s body. It will jump out of the switch body once it matched any case. If you want to continue to matching more cases, you need to use the`fallthrough` statement. +В пятой строке мы поместили несколько значений в один `case`; нам также не надо писать ключевое слово `break` в конце тела `case`. При выполнении какого-либо условия цикл прекратитсяавтоматически. Если Вы хотите продолжать проверку, нужно использовать выражение `fallthrough`. integer := 6 switch integer { @@ -170,39 +171,39 @@ In the fifth line, we put many values in one `case`, and we don't need to add th fmt.Println("default case") } -This program prints the following information. +Эта программа выведет следующее: integer <= 6 integer <= 7 integer <= 8 default case -## Functions +## Функции -Use the `func` keyword to define a function. +Чтобы определить функцию, используйте ключевое слово `func`. func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) { - // function body - // multi-value return + // тело функции + // возврат множества значений return value1, value2 } -We can extrapolate the following information from the example above. +Мы можем сделать вывод из примера выше: -- Use keyword `func` to define a function `funcName`. -- Functions have zero, one or more than one arguments. The argument type comes after the argument name and arguments are separated by `,`. -- Functions can return multiple values. -- There are two return values named `output1` and `output2`, you can omit their names and use their type only. -- If there is only one return value and you omitted the name, you don't need brackets for the return values. -- If the function doesn't have return values, you can omit the return parameters altogether. -- If the function has return values, you have to use the `return` statement somewhere in the body of the function. +- Нужно использовать ключевое слово `func` длы того, чтобы определить функцию `funcName`. +- Функции могут не возвращать аргументов или возвращать один или несколько. Тип аргумента следует после его имени, аргументы разделяются запятой `,`. +- Функции могут возвращать множество значений. +- В примере есть два значение `output1` и `output2`, Вы можете опустить их имена и использовать только типы. +- Если функция возвращает только одно значение, и имя его не указано, можно объявлять его без скобок. +- Если функция не возвращает значений, вы можете вообще опустить параметры return. +- Если функция возвращает значения, нужно обязательно использовать выражение `return` где-нибудь в теле функции. -Let's see one practical example. (calculate maximum value) +Давайте рассмотрим какой-нибудь практический пример: (вычисление максимального значения) package main import "fmt" - // return greater value between a and b + // возвращаем наибольшее значение из a и b func max(a, b int) int { if a > b { return a @@ -215,26 +216,26 @@ Let's see one practical example. (calculate maximum value) y := 4 z := 5 - max_xy := max(x, y) // call function max(x, y) - max_xz := max(x, z) // call function max(x, z) + max_xy := max(x, y) // вызываем функцию max(x, y) + max_xz := max(x, z) // вызываем функцию max(x, z) fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy) fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz) - fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // call function here + fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // вызываем функцию непосредственно отсюда } -In the above example, there are two arguments in the function `max`, their types are both `int` so the first type can be omitted. For instance, `a, b int` instead of `a int, b int`. The same rules apply for additional arguments. Notice here that `max` only has one return value, so we only need to write the type of its return value -this is the short form of writing it. +В приведенном примере функция `max` имеет 2 аргумента, оба аргумента имеют тип `int`, поэтому для первого аргумента указание типа может быть опущено. Например, `a, b int` вместо `a int, b int`. Те же правили применимы для дополнительных аргументов. Имейте в виду, что `max` возвращает только одно значение, поэтому нам нужно указать только тип возвращаемого значения - такова краткая форма записи для таких случаев. -### Multi-value return +### Возврат множества значений -One thing that Go is better at than C is that it supports multi-value returns. +Одна из вещей, в которых Go лучше, чем C - это то, что функции в Go могут возвращать несколько значений. -We'll use the following example here. +Приведем следующий пример: package main import "fmt" - // return results of A + B and A * B + // возвращаем результаты A + B и A * B func SumAndProduct(A, B int) (int, int) { return A+B, A*B } @@ -249,7 +250,7 @@ We'll use the following example here. fmt.Printf("%d * %d = %d\n", x, y, xTIMESy) } -The above example returns two values without names -you have the option of naming them also. If we named the return values, we would just need to use `return` to return the values since they are initialized in the function automatically. Notice that if your functions are going to be used outside of the package, which means your function names start with a capital letter, you'd better write complete statements for `return`; it makes your code more readable. +В вышеприведенном примере два значения возвращаются без имен; также можно и дать им имена. Если мы именуем переменные, которые будут возвращаться, нам нужно лишь написать `return`, чтобы возвратить значения, так как то, что надо возвращать, уже определено в функции автоматически. Имейте в виду, что если Вы собираетесь использовать функцию вне пакета (что означает, что Вы должны именовать эту фунцкию с заглавной буквы), лучше указывавйте полную форму `return`; это сделает Ваш код более читаемым. func SumAndProduct(A, B int) (add int, Multiplied int) { add = A+B @@ -257,85 +258,86 @@ The above example returns two values without names -you have the option of namin return } -### Variable arguments +### Переменные аргументы -Go supports variable arguments, which means you can give an uncertain numbers of argument to functions. +Go поддерживает переменные аргументы, что означает, что можно передать неопределенное количество аргументов в функцию. func myfunc(arg ...int) {} -`arg …int` tells Go that this is a function that has variable arguments. Notice that these arguments are type `int`. In the body of function, the `arg` becomes a `slice` of `int`. +`arg …int` говорит Go о том, что данная функция имеет неопределенное количество аргументов. Заметьте, что в функцию передаются аргументы типа `int`. В теле функции `arg` становится `срезом` элементов типа `int`. for _, n := range arg { - fmt.Printf("And the number is: %d\n", n) + fmt.Printf("И число равно: %d\n", n) } -### Pass by value and pointers +### Передача аргументов по значению и указателю -When we pass an argument to the function that was called, that function actually gets the copy of our variables so any change will not affect to the original variable. +Когда мы передаем в функцию аргументы, на самом деле она получает копию передаваемых переменных, поэтому любое изменение не затронет оригинал переданной переменной. -Let's see one example in order to prove what i'm saying. +Чтобы доказать это, рассмотрим следующий пример: package main import "fmt" - // simple function to add 1 to a + // простая функция, прибавляющая 1 к a func add1(a int) int { - a = a+1 // we change value of a - return a // return new value of a + a = a+1 // мы изменяем значение a + return a // возвращаем новое значение a } func main() { x := 3 - fmt.Println("x = ", x) // should print "x = 3" + fmt.Println("x = ", x) // должно печатать "x = 3" - x1 := add1(x) // call add1(x) + x1 := add1(x) // вызываем add1(x) - fmt.Println("x+1 = ", x1) // should print "x+1 = 4" - fmt.Println("x = ", x) // should print "x = 3" + fmt.Println("x+1 = ", x1) // должно печатать "x+1 = 4" + fmt.Println("x = ", x) // должно печатать "x = 3" } -Can you see that? Even though we called `add1` with `x`, the origin value of `x` doesn't change. +Видите? Несмотря на то, что мы вызвали функцию `add1` с `x`, изначальное значение `x` не изменилось. -The reason is very simple: when we called `add1`, we gave a copy of `x` to it, not the `x` itself. +Причина очерь проста: когда мы вызвали `add1`, мы передали в нее копию `x`, а не сам `x`. -Now you may ask how I can pass the real `x` to the function. +Теперь Вы можете спросить, как передать в функцию сам `x`? -We need use pointers here. We know variables are stored in memory and they have some memory addresses. So, if we want to change the value of a variable, we must change its memory address. Therefore the function `add1` has to know the memory address of `x` in order to change its value. Here we pass `&x` to the function, and change the argument's type to the pointer type `*int`. Be aware that we pass a copy of the pointer, not copy of value. +В этом случе нам нужно использовать указатели. Мы знаем, что переменные хранятся в памяти и у них есть адреса. Итак, если мы хотим изменить значение переменной, мы меняем значение, находящееся в памяти по соответствующему ей адресу. Поэтому, для того, чтобы изменить значение `x`, `add1` должна знать адрес `x` в памяти. Здесь мы передаем `&x` в функцию и меняем тип аргумента на тип указателя `*int`. Мы передаем в функцию копию указателя, не копию значения. package main import "fmt" - // simple function to add 1 to a + // простая функция, которая прибавляет 1 к a func add1(a *int) int { - *a = *a+1 // we changed value of a - return *a // return new value of a + *a = *a+1 // мы изменяем a + return *a // мы возвращаем значение a } func main() { x := 3 - fmt.Println("x = ", x) // should print "x = 3" + fmt.Println("x = ", x) // должно печатать "x = 3" - x1 := add1(&x) // call add1(&x) pass memory address of x + x1 := add1(&x) // вызываем add1(&x), передаем значение адреса памяти для x - fmt.Println("x+1 = ", x1) // should print "x+1 = 4" - fmt.Println("x = ", x) // should print "x = 4" + fmt.Println("x+1 = ", x1) // должно печатать "x+1 = 4" + fmt.Println("x = ", x) // должно печатать "x = 4" } -Now we can change the value of `x` in the functions. Why do we use pointers? What are the advantages? +Зная все это, можно изменять значение `x` в функциях. Зачем использовать указатели? Каковы преимущества? -- Allows us to use more functions to operate on one variable. +- Это позволяет многим функциям работать с одной переменной. - Low cost by passing memory addresses (8 bytes), copy is not an efficient way, both in terms of time and space, to pass variables. -- `string`, `slice`, `map` are reference types, so they use pointers when passing to functions by default. (Attention: If you need to change the length of `slice`, you have to pass pointers explicitly) +- Низкая стоимость выполнения благодаря тому, что передаются лишь адреса памяти (8 байт); копирование самих переменных не является эффективным как с точки зрения времени, так и объема памяти. +- `string`, `slice`, `map` - это ссылочные типы, поэтому они передаются в функцию как указатели по умолчанию. (Внимание: Если Вам нужно изменить длину `среза(slice)`, нужно явно передать срез как указатель) ### defer -Go has a well designed keyword called `defer`. You can have many `defer` statements in one function; they will execute in reverse order when the program executes to the end of functions. In the case where the program opens some resource files, these files would have to be closed before the function can return with errors. Let's see some examples. +В Go есть хорошо спроектированное ключевое слово `defer`. В одной функции может быть много выражений `defer`; они будут выполняться в обратном порядке в тот момент, когда процесс выполнения программы дойдет до конца функции. Рассмотрим случай: когда программа открывает какие-либо файлы, они затем должны быть закрыты перед тем, как функция закончит свою работу с ошибкой. Давайте взглянем на примеры: func ReadWrite() bool { file.Open("file") - // Do some work + // Что-нибудь делаем (failureX и failureY - условия, свидетельствующие о том, что произошли ошибки - прим. переводчика на русский) if failureX { file.Close() return false @@ -350,7 +352,7 @@ Go has a well designed keyword called `defer`. You can have many `defer` stateme return true } -We saw some code being repeated several times. `defer` solves this problem very well. It doesn't only help you to write clean code but also makes your code more readable. +Мы видим, что один и тот же код повторился несколько раз. `defer` просто решает эту проблему. Оно не только помогает Вам писать чистый код, но и делает его более читаемым. func ReadWrite() bool { file.Open("file") @@ -364,24 +366,24 @@ We saw some code being repeated several times. `defer` solves this problem very return true } -If there are more than one `defer`s, they will execute by reverse order. The following example will print `4 3 2 1 0`. +Если присутствует больше одного `defer`, они будут выполняться в обратном порядке. Следующий пример выведет `4 3 2 1 0`. for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i) } -### Functions as values and types +### Функции как значение и типы -Functions are also variables in Go, we can use `type` to define them. Functions that have the same signature can be seen as the same type. +В Go функции также являются переменными, мы можем использовать `type`, чтобы их определять. Функции с идентичными подписями являются функциями одного типа: type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...]) -What's the advantage of this feature? The answer is that it allows us to pass functions as values. +В чем преимущества такого способа? Ответ состоит в том, что это позволяет передавать функции как значения в другие функции. package main import "fmt" - type testInt func(int) bool // define a function type of variable + type testInt func(int) bool // определяем тип переменной "функция" func isOdd(integer int) bool { if integer%2 == 0 { @@ -397,7 +399,7 @@ What's the advantage of this feature? The answer is that it allows us to pass fu return false } - // pass the function `f` as an argument to another function + // передаем функцию `f` как аргумент в другую функцию func filter(slice []int, f testInt) []int { var result []int @@ -411,34 +413,34 @@ What's the advantage of this feature? The answer is that it allows us to pass fu func main(){ slice := []int {1, 2, 3, 4, 5, 7} - fmt.Println("slice = ", slice) - odd := filter(slice, isOdd) // use function as values - fmt.Println("Odd elements of slice are: ", odd) + fmt.Println("Срез = ", slice) + odd := filter(slice, isOdd) // используем функции как значения + fmt.Println("Нечетные элементы среза: ", odd) even := filter(slice, isEven) - fmt.Println("Even elements of slice are: ", even) + fmt.Println("Четные элементы среза: ", even) } -It's very useful when we use interfaces. As you can see `testInt` is a variable that has function type, and return values and arguments of `filter` are the same as `testInt`. Therefore, we can have complex logic in our programs, while maintaining flexibility in our code. +Это свойство очень полезно, когда мы используем интерфейсы. Как мы можем видеть, `testInt` - это переменная, имеющая тип "функция", аргументы и возвращаемые значение `filter` те же самые, что и `testInt` (здесь не согласен с оригиналом - прим. переводчика на русский). Поэтому мы можем применять в своих программах сложную логику, подерживая гибкость нашего кода. -### Panic and Recover +### Panic и Recover -Go doesn't have `try-catch` structure like Java does. Instead of throwing exceptions, Go uses `panic` and `recover` to deal with errors. However, you shouldn't use `panic` very much, although it's powerful. +В Go, в отличии от Java, нет структуры `try-catch`. Вместо того, чтобы "кидать" исключения, для работы с ошибками Go использует `panic` и `recover`. Однако, не стоит использовать `panic` слишком много, несмотря на его мощность. -Panic is a built-in function to break the normal flow of programs and get into panic status. When a function `F` calls `panic`, `F` will not continue executing but its `defer` functions will continue to execute. Then `F` goes back to the break point which caused the panic status. The program will not terminate until all of these functions return with panic to the first level of that `goroutine`. `panic` can be produced by calling `panic` in the program, and some errors also cause `panic` like array access out of bounds errors. +Panic - это встроенная функция, которая прерыавает ход программы и включает статус "паники". Когда функция `F` вызывает `panic`, `F` не продолжит после этого свое исполнение, но функции `defer` выполняться. Затем `F` возвращается к той точке своего выполнения, где была вызвана panic. Пока все функции не вернут panic функциям уровнем выше, которые их вызвали, программа не прервет своего выполнения. `panic` может произойти в результате вызова `panic` в программе, также некоторыен ошибки вызывают `panic` как, например, при попытке доступа к массиву за его пределами. -Recover is a built-in function to recover `goroutine`s from panic status. Calling `recover` in `defer` functions is useful because normal functions will not be executed when the program is in the panic status. It catches `panic` values if the program is in the panic status, and it gets `nil` if the program is not in panic status. +Recover - это встроенная функция для восстановления `горутин` из состояния panic. Нормально будет вызывать `recover` в функциях `defer`, так как обычные функции не буду выполняться, если программа находится в состоянии panic. Эта функция получает значение `panic`, если программа находится в состоянии panic, и `nil`, если не находится. -The following example shows how to use `panic`. +Следующий пример показывает, как использовать `panic`. var user = os.Getenv("USER") func init() { if user == "" { - panic("no value for $USER") + panic("не присвоено значение переменной $USER") } } -The following example shows how to check `panic`. +Следующий пример показывает, как проверять `panic`. func throwsPanic(f func()) (b bool) { defer func() { @@ -446,71 +448,72 @@ The following example shows how to check `panic`. b = true } }() - f() // if f causes panic, it will recover + f() // если f вызывает panic, включается recover return } -### `main` function and `init` function +### функции `main` и `init` -Go has two retentions which are called `main` and `init`, where `init` can be used in all packages and `main` can only be used in the `main` package. These two functions are not able to have arguments or return values. Even though we can write many `init` functions in one package, I strongly recommend writing only one `init` function for each package. +В Go есть две зарезервированные функции - `main` и `init`, причем `init` может быть использована во всех пакетах, а `main` - только в пакете `main`. У этих функций не может быть аргументов и возвращаемых значений. Даже несмотря на то, что можно использовать несколько функций `init` в одном пакете, я настоятельно рекомендую использовать только по одной функции `init` для каждого пакета. -Go programs will call `init()` and `main()` automatically, so you don't need to call them by yourself. For every package, the `init` function is optional, but `package main` has one and only one `main` function. +Программы на Go вызывают `init()` и `main()` автоматически, поэтому не нужно запускать их самому. Функция `init` может присутствовать в пакете, а может и не присутствовать, но, что касается функции `main`, то она обязана быть в `package main`, причем только в одном экземпляре. Programs initialize and begin execution from the `main` package. If the `main` package imports other packages, they will be imported in the compile time. If one package is imported many times, it will be only compiled once. After importing packages, programs will initialize the constants and variables within the imported packages, then execute the `init` function if it exists, and so on. After all the other packages are initialized, programs will initialize constants and variables in the `main` package, then execute the `init` function inside the package if it exists. The following figure shows the process. +Программа инициализируется и начинает свое выполнение с пакета `main`. Если пакет `main` импортирует другие пакеты, они будут импортированы во время компиляции. Если один пакет импортируется несколько раз, он будет скомпилирован лишь единожды. После импорта пакета программа инициализирует переменные и константы в импортированном пакете, а затем выполнит функцию `init`, если она присутствует, и т.д. После того, как все пакеты будут проинициализированы, программа инициализирует константы и переменные в пакете `main`, а затем выполнит функцию `init` внутри него, если она имеется. Весь процесс изображен на следующем рисунке: ![](images/2.3.init.png?raw=true) -Figure 2.6 Flow of programs initialization in Go +Рисунок 2.6 Ход инициализации программы в Go ### import -We use `import` very often in Go programs as follows. +`import` очень часто используется в Go следующим образом: import( "fmt" ) -Then we use functions in that package as follows. +Вот так используются функции из импортированного пакета: fmt.Println("hello world") -`fmt` is from Go standard library, it is located within $GOROOT/pkg. Go supports third-party packages in two ways. +`fmt` находится в стандртной библиотеке Go, он располагается в $GOROOT/pkg. Go поддерживает сторонние пакеты двумя способами: -1. Relative path - import "./model" // load package in the same directory, I don't recommend this way. -2. Absolute path - import "shorturl/model" // load package in path "$GOPATH/pkg/shorturl/model" +1. Относительный путь + import "./model" // импортирует пакет из той же директории, где находится программа, я не рекомендую этот способ. +2. Абсолютный путь + import "shorturl/model" // импортирует пакет, находящийся по пути "$GOPATH/pkg/shorturl/model" -There are some special operators when we import packages, and beginners are always confused by these operators. +Существует несколько сспециальных операторов, относящихся к импорту пакетов, и новички в них постоянно путаются: -1. Dot operator. - Sometime we see people use following way to import packages. +1. Оператор "Точка". + Иногда мы можем видеть, как пакеты импортируются так: import( . "fmt" ) - The dot operator means you can omit the package name when you call functions inside of that package. Now `fmt.Printf("Hello world")` becomes to `Printf("Hello world")`. -2. Alias operation. - It changes the name of the package that we imported when we call functions that belong to that package. + Оператор "Точка" означает, что можно опускать имя пакета при использовании функций этого пакета. Теперь `fmt.Printf("Hello world")` превращается в `Printf("Hello world")`. +2. Операция с псеводнимом. + Она изменяет имя пакета при использовании функций из него: import( f "fmt" ) - Now `fmt.Printf("Hello world")` becomes to `f.Printf("Hello world")`. -3. `_` operator. - This is the operator that is difficult to understand without someone explaining it to you. + Теперь вместо `fmt.Printf("Hello world")` можно `f.Printf("Hello world")`. +3. Оператор `_`. + Этот оператор трудно понять без объяснения. import ( "database/sql" _ "github.com/ziutek/mymysql/godrv" ) - The `_` operator actually means we just want to import that package and execute its `init` function, and we are not sure if want to use the functions belonging to that package. + Оператор `_` означает, что мы просто хотим импортировать пакет и выполнить его функцию `init`, но не уверены, будем ли мы использовать фунцкии, которые он содержит. -## Links +## Ссылки -- [Directory](preface.md) -- Previous section: [Go foundation](02.2.md) -- Next section: [struct](02.4.md) +- [Содержание](preface.md) +- Предыдущий раздел: [Фундамент Go](02.2.md) +- Следующий раздел: [struct](02.4.md) diff --git a/ru/preface.md b/ru/preface.md index 670fa0c..cec43ef 100644 --- a/ru/preface.md +++ b/ru/preface.md @@ -7,7 +7,7 @@ - 2.[Основы Go](02.0.md) - 2.1. ["Hello, Go"](02.1.md) - 2.2. [Фундамент Go](02.2.md) - - 2.3. [Условные операторы и функции](02.3.md) + - 2.3. [Управляющие конструкции и функции](02.3.md) - 2.4. [struct](02.4.md) - 2.5. [Object-oriented](02.5.md) - 2.6. [interface](02.6.md) -- GitLab