Avatar ·

Как создавать и использовать свои собственные пакеты в Go?

Всем привет! Я только начал изучать Go, до этого писал на Python. Там я мог просто создать файл и импортировать его через `import my_file`. В Go я совсем запутался с пакетами. У меня есть проект, структура такая: ``` myproject/ ├── main.go └── math/ └── helpers.go ``` В `helpers.go` я хочу написать простую функцию сложения: ```go package math func Add(a int, b int) int { return a + b } ``` А в `main.go` пытаюсь ее использовать: ```go package main import "myproject/math" func main() { sum := math.Add(2, 3) fmt.Println(sum) } ``` Но компилятор выдает ошибку: `could not import "myproject/math"`. Я пробовал разные пути в import, ставил `go mod init`, но все равно не работает. Что я делаю не так? Как правильно организовать код в Go, чтобы можно было использовать свои пакеты? Объясните, пожалуйста, для новичка, с примерами. <<>>

Основы работы с пакетами в Go

Проблема в том, что Go использует модули и GOPATH для поиска пакетов. В современных версиях Go (1.11+) правильный способ — использовать модули. Давайте разберем пошагово.

Шаг 1: Инициализация модуля

В корне проекта (myproject/) выполните команду:

go mod init myproject

Это создаст файл go.mod с содержимым:

module myproject

go 1.21

Теперь Go знает, что ваш проект называется myproject и все импорты будут относительно этого имени.

Шаг 2: Правильная структура пакета

Пакет math должен находиться в подпапке math/. Ваш файл helpers.go уже правильный:

// math/helpers.go
package math

func Add(a int, b int) int {
    return a + b
}

Важно: имя пакета (после package) должно совпадать с именем папки. В Go принято давать пакетам короткие имена (одно слово, нижний регистр).

Шаг 3: Импорт в main.go

Теперь в main.go нужно импортировать пакет с полным путем, начиная от имени модуля:

// main.go
package main

import (
    "fmt"
    "myproject/math"  // имя_модуля/имя_папки
)

func main() {
    sum := math.Add(2, 3)
    fmt.Println("Сумма:", sum)  // Вывод: Сумма: 5
}

Шаг 4: Экспортируемые и неэкспортируемые функции

В Go функции и переменные, начинающиеся с заглавной буквы, являются экспортируемыми (публичными). Со строчной — приватными для пакета.

// math/helpers.go
package math

// Экспортируемая функция (с большой буквы)
func Add(a int, b int) int {
    return a + b
}

// Приватная функция (с маленькой буквы)
func subtract(a int, b int) int {
    return a - b
}

Шаг 5: Несколько файлов в одном пакете

В одном пакете может быть много файлов. Например, создайте math/multiply.go:

// math/multiply.go
package math

func Multiply(a int, b int) int {
    return a * b
}

Теперь в main.go можно использовать обе функции:

sum := math.Add(2, 3)
prod := math.Multiply(2, 3)

Шаг 6: Вложенные пакеты

Можно создавать вложенные пакеты. Например, math/advanced/:

// math/advanced/trig.go
package advanced

func Sin(x float64) float64 {
    return x * 3.14 / 180  // упрощенно
}

Импорт:

import "myproject/math/advanced"

Важные правила:

  • Один пакет — одна папка. Нельзя в одной папке иметь два разных пакета.
  • Имя папки = имени пакета. Это конвенция, но не жесткое правило. Лучше следовать ей.
  • Циклические импорты запрещены. Пакет A не может импортировать пакет B, если B импортирует A.
  • Используйте go mod tidy для синхронизации зависимостей.

Полный рабочий пример:

myproject/
├── go.mod          (module myproject)
├── main.go
└── math/
    ├── helpers.go  (package math)
    └── multiply.go (package math)

// go.mod
module myproject
go 1.21

// main.go
package main
import (
    "fmt"
    "myproject/math"
)
func main() {
    fmt.Println(math.Add(5, 3))
    fmt.Println(math.Multiply(5, 3))
}

// math/helpers.go
package math
func Add(a, b int) int { return a + b }

// math/multiply.go
package math
func Multiply(a, b int) int { return a * b }

После этого запустите go run main.go — всё должно работать!

<<>>

Альтернативный подход: локальные пакеты с replace в go.mod

Если вы разрабатываете несколько проектов и хотите переиспользовать пакеты между ними, не публикуя их на GitHub, используйте директиву replace.

Пример:

У вас есть общий пакет mylib в папке ../shared/mylib:

// ../shared/mylib/strings.go
package mylib

func Reverse(s string) string {
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}

В файле go.mod вашего проекта укажите:

module myproject

go 1.21

require mylib v0.0.0

replace mylib => ../shared/mylib

Теперь можно импортировать:

import "mylib"

func main() {
    fmt.Println(mylib.Reverse("привет"))
}

Преимущества:

  • Не нужно копировать код между проектами.
  • Изменения в mylib сразу доступны всем проектам.
  • Можно использовать для временных или внутренних библиотек.

Недостатки:

  • Сложнее деплоить — нужно либо копировать папку, либо публиковать модуль.
  • Может возникнуть путаница с версиями.

Этот подход удобен для монолитов или микросервисов в рамках одного репозитория.