Урок 4 из 8 · 25 мин · обновлено
Функции и обработка ошибок
Как создавать функции с параметрами и типами, возвращать несколько значений и почему в Go ошибки — это обычные значения, которые проверяют через if err != nil.
Функции
Функция — именованный кусок кода, который можно вызывать много раз. Вы уже пользовались готовыми, например fmt.Println(). Свою создают через func:
func greet(name string) {
fmt.Println("Здравствуйте,", name)
}
func add(a, b int) int {
return a + b
}
- в скобках — параметры с типами. Если у соседних параметров тип один, его пишут один раз:
a, b int; - после скобок — тип результата. У
greetего нет: она ничего не возвращает; returnвозвращает результат туда, где функцию вызвали.
Функции объявляют вне main, а вызывают по имени:
greet("Анна")
fmt.Println(add(2, 3))
Здравствуйте, Анна
5
Несколько результатов
Функция в Go может вернуть сразу несколько значений:
func divide(a, b int) (int, int) {
return a / b, a % b
}
q, r := divide(17, 5)
fmt.Println(q, r)
3 2
Если одно из значений не нужно, вместо имени ставят подчёркивание _. Неиспользованная переменная в Go — ошибка компиляции, а _ говорит: «значение знаю, но оно мне не нужно».
_, rest := divide(17, 5)
fmt.Println(rest)
2
Ошибки — это значения
В Go нет исключений, которые внезапно обрывают программу. Если функция может не сработать, она возвращает два значения: результат и ошибку типа error. Например, strconv.Atoi превращает текст в число:
n, err := strconv.Atoi("42")
fmt.Println(n, err)
n, err = strconv.Atoi("сорок два")
fmt.Println(n, err)
42 <nil>
0 strconv.Atoi: parsing "сорок два": invalid syntax
nil означает «ничего»: ошибки нет. Поэтому после такого вызова сразу проверяют ошибку:
age, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Введите число, а не", input)
return
}
fmt.Println("Возраст:", age)
Запись if err != nil вы будете видеть в коде на Go постоянно. Сначала кажется, что её слишком много. Зато по коду всегда видно, где что-то может пойти не так и что программа будет делать в этом случае.
Свои ошибки
Свою функцию с ошибкой пишут так же: последним результатом объявляют error.
func withdraw(balance, amount int) (int, error) {
if amount > balance {
return balance, errors.New("недостаточно средств")
}
return balance - amount, nil
}
b, err := withdraw(500, 800)
if err != nil {
fmt.Println("Ошибка:", err)
}
fmt.Println(b)
Ошибка: недостаточно средств
500
errors.New("…")создаёт ошибку с текстом, для этого подключают пакетerrors;fmt.Errorf("…")делает то же, но позволяет вставить значения, какPrintf;- если всё хорошо, вместо ошибки возвращают
nil.
Попробуйте сами. Напишите функцию discount(price, percent int) (int, error). Она возвращает цену со скидкой, а если скидка меньше 0 или больше 100 — ошибку. Проверьте: discount(1000, 15) даёт 850, а discount(1000, 120) — ошибку.
Решение
package main
import "fmt"
func discount(price, percent int) (int, error) {
if percent < 0 || percent > 100 {
return 0, fmt.Errorf("скидка %d%% вне диапазона 0–100", percent)
}
return price * (100 - percent) / 100, nil
}
func main() {
fmt.Println(discount(1000, 15))
price, err := discount(1000, 120)
if err != nil {
fmt.Println("Ошибка:", err)
return
}
fmt.Println(price)
}
850 <nil>
Ошибка: скидка 120% вне диапазона 0–100
В шаблоне %% выводит сам знак процента. При ошибке функция возвращает 0, но вызывающий код сначала смотрит на err и только потом пользуется ценой.
Мини-тест: проверьте себя
Закрепите урок: несколько вопросов, разбор ответов — в конце.
-
Как правильно объявить функцию, которая складывает два целых числа?
В Go у параметров указывают тип, а после скобок — тип результата. Если у соседних параметров тип одинаковый, его можно написать один раз: a, b int.
-
Функция strconv.Atoi("сорок два") вернула два значения. Что в них будет?
Atoi не может превратить такой текст в число, поэтому возвращает 0 и ошибку: parsing "сорок два": invalid syntax. Программа продолжает работать, а решать, что делать, — вам.
-
Что означает, что функция вернула ошибку, равную nil?
nil — это «ничего». Если в переменной err ничего нет, значит всё прошло успешно.
-
Зачем в записи _, rest := divide(17, 5) стоит подчёркивание?
Подчёркивание — «пустое имя». Значение приходит, но никуда не сохраняется, и компилятор не ругается на неиспользуемую переменную.