Golang: Функции
Функции в Go объявляются с помощью ключевого слова func
. Пример простой функции умножения:
func multiply(x int, y int) int {
return x * y
}
Сигнатура функции состоит из следующих элементов:
-
func
— ключевое слово для объявления функции; -
multiply
— имя функции; -
(x int, y int)
— параметры с указанием типов. Если несколько параметров подряд имеют одинаковый тип, запись можно сократить:func multiply(x, y int) int
-
int
— возвращаемый тип; -
{ ... }
— тело функции.
В Go обязательно указывать тип каждого параметра и результат, язык не допускает неявных объявлений. Порядок тоже строгий — тип всегда идёт после имени параметра.
Нет параметров по умолчанию
В Go не поддерживаются параметры по умолчанию, в отличие от многих других языков программирования. То есть, если функция принимает два аргумента, вы обязаны передать оба значения при вызове.
В языках где нет параметров по умолчанию, часто, применяют подход с созданием функции-обертки, которая позволяет эмулировать это поведение. Допустим, у нас есть функция getGreeting(name, greeting string)
, которая формирует приветствие:
func GetGreeting(name string, greeting string) string {
return greeting + ", " + name
}
В Go нельзя сделать greeting необязательным, но мы можем создать обёртку:
// Обёртка: использует "Hello" как greeting по умолчанию
func GetGreetingDefault(name string) string {
return getGreeting(name, "Hello")
}
Вызов внешних функций
Функции, объявленные в одном и том же пакете, можно вызывать просто по имени:
func greet(name string) string {
return "Hello, " + name
}
func main() {
msg := greet("Hexlet")
fmt.Println(msg)
}
Если функция определена в другом пакете, её нужно вызывать с указанием имени пакета:
package main
import "fmt"
func main() {
fmt.Println("Hello from Hexlet")
}
Здесь Println
— функция из пакета fmt
. Мы обращаемся к ней как fmt.Println
, потому что она объявлена в другом пакете.
Встроенные и внешние пакеты
В Go есть встроенные пакеты — они входят в стандартную библиотеку. Например:
fmt
— для форматированного выводаstrings
— для работы со строкамиmath
— для базовой математикиtime
— для работы со временем
Эти пакеты можно подключать через import
без установки дополнительных зависимостей. Кроме встроенных, можно использовать внешние пакеты — они подключаются отдельно через систему модулей (go mod). Работа с внешними пакетами рассматривается в соответствующем курсе на Хекслете.
Именование и доступность
Именование функций в Go следует стилю camelCase. Это означает, что имена пишутся слитно, а каждое новое слово в имени начинается с заглавной буквы: printMessage
, processData
, sendEmail
.
С первой буквой в названиях все чуть сложнее. Ее регистр определяет доступность функции снаружи пакета. Если имя начинается с заглавной буквы, функция доступна из других пакетов (экспортируемая). Если с маленькой, она остаётся внутри текущего пакета.
package math
func Multiply(x, y int) int {
// публичная
// экспортируемая функция, доступна как math.Multiply(...)
}
func divide(x, y int) int {
// приватная
// неэкспортируемая функция, доступна только внутри math
}
Так устроена видимость по аналогии с модификаторами доступа в других языках, но без ключевых слов вроде public или private — всё управляется регистром первой буквы.
Множественные возвращаемые значения
Go поддерживает возвращение нескольких значений из функции. Наиболее частый сценарий — возврат результата и ошибки. Работу с ошибками мы рассмотрим чуть позже, здесь же код дается просто понимания идеи.
func divide(x, y int) (int, error) {
if y == 0 {
return 0, errors.New("cannot divide by zero")
}
return x / y, nil
}
Здесь функция возвращает два значения: результат деления и объект ошибки (или nil, если ошибки нет). Сигнатура функции теперь содержит несколько типов в круглых скобках после func divide(...)
— в данном случае это (int, error)
.
Такой подход заменяет исключения (try/catch)
и делает обработку ошибок явной. Go заставляет вызывать сторону явно проверять возвращаемую ошибку, что снижает вероятность игнорирования важных условий.
Задание
Реализуйте функцию Double()
, которая принимает на вход целое число и возвращает его удвоенное значение.
Примеры
Double(3) // 6
Double(8) // 16
Полезное
Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект
Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "ИИ-помощник":
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи. В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в обратной связи нашего сообщества
Ваше упражнение проверяется по этим тестам
package solution
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestDouble(t *testing.T) {
a := assert.New(t)
a.Equal(6, Double(3))
a.Equal(16, Double(8))
a.Equal(0, Double(0))
a.Equal(-4, Double(-2))
}
Решение учителя откроется через:
20:00
