Пакет Go состоит из Go-файлов, расположенных в одной и той же директории, в начале которых имеется одинаковое выражение
![](https://habrastorage.org/r/w780q1/webt/ht/dv/4c/htdv4cfnn8nmki3yphorht5eiki.jpeg)
Материал, перевод которого мы сегодня публикуем, представляет собой руководство по разработке Go-пакетов, которые можно подключать к другим файлам.
Написание кода пакета — это то же самое, что и написание обычного кода на Go. Пакеты могут содержать объявления функций, типов и переменных, которые потом могут быть использованы в других Go-программах.
Прежде чем мы сможем приступить к созданию нового пакета, нам нужно перейти в рабочее пространство Go. Оно находится по пути, задаваемом переменной
Мы собираемся назвать пакет, который будем разрабатывать в этом руководстве,
Теперь мы готовы к тому, чтобы добавить в директорию
В этом файле мы можем писать код, который хотим многократно использовать в наших проектах. В данном случае мы создадим функцию
Откройте файл
Разберём содержимое этого файла. Первая строка каждого файла должна содержать имя пакета, в котором мы работаем. Так как мы находимся в пакете
Это сообщает компилятору о том, что он должен воспринимать всё, что находится в файле, как часть пакета
Далее выполняется импорт необходимых пакетов с помощью выражения
И, наконец, мы создаём функцию
Теперь, после того, как создан пакет
А именно, мы создадим пакет
Теперь создаём файл, являющийся входной точкой пакета. Данный пакет мы рассматриваем как выполняемую программу, а не как пакет, код которого планируется использовать в других пакетах. Файлы, являющиеся входными точками программ, принято называть
Откройте в редакторе файл
Мы импортировали в файле
Теперь можно открыть терминал и запустить программу:
После того, как вы это сделаете, в терминале будет выведено следующее:
Теперь поговорим о том, как использовать переменные, объявляемые в пакетах. Для этого добавим объявление переменной в файл
Откройте файл
Снова запустите программу:
Теперь она выведет следующее:
А сейчас поговорим о том, как объявлять в пакетах типы. Создадим тип
Теперь откройте
После того, как вы, с помощью конструкции, которая выглядит как
Метод
Запустим программу снова:
Она выведет в консоль следующее:
Теперь, когда мы оснастили Octopus методом
Возможно, вы обратили внимание на то, что всё, с чем мы работали, обращаясь к пакету
Если оснастить тип
Попытаемся вызвать
Это приведёт к появлению следующей ошибки компиляции:
Для того чтобы экспортировать метод
Это приведёт к тому, что мы сможем вызывать
Запустим программу:
Вот что попадёт в консоль:
Вызвав метод
Написание пакетов Go ничем не отличается от написания обычного Go-кода. Однако размещение кода пакетов в собственных директориях позволяет изолировать код, которым можно воспользоваться в любых других Go-проектах. Здесь мы поговорили о том, как объявлять в пакетах функции, переменные и типы, рассмотрели порядок использования этих сущностей за пределами пакетов, разобрались с тем, где нужно хранить пакеты, рассчитанные на их многократное использование.
Уважаемые читатели! Какие программы вы обычно пишете на Go? Пользуетесь ли вы в них пакетами собственной разработки?
![](https://habrastorage.org/r/w1560/webt/it/t5/3p/itt53pns2iucwylb3bwn1fmmtnu.png)
package
. Пакеты, подключаемые к программам, позволяют расширять их возможности. Некоторые пакеты входят в состав стандартной библиотеки Go. А это значит, что они, если вы пользуетесь Go, уже у вас установлены. Другие пакеты устанавливают с помощью команды go get
. Можно, кроме того, писать собственные Go-пакеты, создавая файлы в особых директориях, и придерживаясь правил оформления пакетов.![](https://habrastorage.org/webt/ht/dv/4c/htdv4cfnn8nmki3yphorht5eiki.jpeg)
Материал, перевод которого мы сегодня публикуем, представляет собой руководство по разработке Go-пакетов, которые можно подключать к другим файлам.
Предварительные требования
- Настройте программное окружение Go (о том, как это сделать, можно узнать здесь). Создайте рабочее пространство Go (этому посвящён пятый пункт вышеупомянутого материала). В следующем разделе этого материала вы сможете найти примеры, которые рекомендуется воспроизвести у себя. Так вы сможете лучше с ними разобраться.
- Для того чтобы углубить свои знания по
GOPATH
— взгляните на этот материал.
Написание и импорт пакетов
Написание кода пакета — это то же самое, что и написание обычного кода на Go. Пакеты могут содержать объявления функций, типов и переменных, которые потом могут быть использованы в других Go-программах.
Прежде чем мы сможем приступить к созданию нового пакета, нам нужно перейти в рабочее пространство Go. Оно находится по пути, задаваемом переменной
GOPATH
. Например, пусть наша организация называется gopherguides
. При этом мы, в качестве репозитория, используем GitHub
. Это приводит к тому, что у нас, по пути, задаваемом GOPATH
, имеется следующая структура папок:└── $GOPATH
└── src
└── github.com
└── gopherguides
Мы собираемся назвать пакет, который будем разрабатывать в этом руководстве,
greet
. Для того чтобы это сделать — создадим директорию greet
в директории gopherguides
. В результате имеющаяся структура папок приобретёт следующий вид:└── $GOPATH
└── src
└── github.com
└── gopherguides
└── greet
Теперь мы готовы к тому, чтобы добавить в директорию
greet
первый файл. Обычно файл, который является входной точкой (entry point) пакета, называют так же, как названа директория пакета. В данном случае это означает, что мы, в директории greet
, создаём файл greet.go
:└── $GOPATH
└── src
└── github.com
└── gopherguides
└── greet
└── greet.go
В этом файле мы можем писать код, который хотим многократно использовать в наших проектах. В данном случае мы создадим функцию
Hello
, которая выводит текст Hello, World!
.Откройте файл
greet.go
в текстовом редакторе и добавьте в него следующий код:package greet
import "fmt"
func Hello() {
fmt.Println("Hello, World!")
}
Разберём содержимое этого файла. Первая строка каждого файла должна содержать имя пакета, в котором мы работаем. Так как мы находимся в пакете
greet
— здесь используется ключевое слово package
, за которым следует имя пакета:package greet
Это сообщает компилятору о том, что он должен воспринимать всё, что находится в файле, как часть пакета
greet
.Далее выполняется импорт необходимых пакетов с помощью выражения
import
. В данном случае нам нужен всего один пакет — fmt
:import "fmt"
И, наконец, мы создаём функцию
Hello
. Она будет использовать возможности пакета fmt
для вывода на экран строки Hello, World!
:func Hello() {
fmt.Println("Hello, World!")
}
Теперь, после того, как создан пакет
greet
, вы можете воспользоваться им в любом другом пакете. Создадим новый пакет, в котором воспользуемся пакетом greet
.А именно, мы создадим пакет
example
. Для этого будем исходить из тех же предположений, из которых исходили, создавая пакет greet
. Для начала создадим папку example
в папке gopherguides
:└── $GOPATH
└── src
└── github.com
└── gopherguides
└── example
Теперь создаём файл, являющийся входной точкой пакета. Данный пакет мы рассматриваем как выполняемую программу, а не как пакет, код которого планируется использовать в других пакетах. Файлы, являющиеся входными точками программ, принято называть
main.go
:└── $GOPATH
└── src
└── github.com
└── gopherguides
└── example
└── main.go
Откройте в редакторе файл
main.go
и внесите в него следующий код, который позволяет воспользоваться возможностями пакета greet
:package main
import "github.com/gopherguides/greet"
func main() {
greet.Hello()
}
Мы импортировали в файле
main.go
пакет greet
, а это значит, что для вызова функции, объявленной в этом пакете, нам понадобится воспользоваться точечной нотацией. Точечная нотация — это конструкция, в которой между именем пакета и именем ресурса этого пакета, который нужно использовать, ставится точка. Например, в пакете greet
роль ресурса играет функция Hello
. Если нужно вызвать эту функцию — используется точечная нотация: greet.Hello()
.Теперь можно открыть терминал и запустить программу:
go run main.go
После того, как вы это сделаете, в терминале будет выведено следующее:
Hello, World!
Теперь поговорим о том, как использовать переменные, объявляемые в пакетах. Для этого добавим объявление переменной в файл
greet.go
:package greet
import "fmt"
var Shark = "Sammy"
func Hello() {
fmt.Println("Hello, World!")
}
Откройте файл
main.go
и добавьте в него строку, в которой функция fmt.Println()
используется для вывода значения переменной Shark
, объявленной в пакете greet.go
. А именно, приведите main.go
к следующему виду:package main
import (
"fmt"
"github.com/gopherguides/greet"
)
func main() {
greet.Hello()
fmt.Println(greet.Shark)
}
Снова запустите программу:
go run main.go
Теперь она выведет следующее:
Hello, World!
Sammy
А сейчас поговорим о том, как объявлять в пакетах типы. Создадим тип
Octopus
с полями Name
и Color
, а также создадим метод типа. Этот метод, при его вызове, будет возвращать особым образом обработанное содержимое полей типа Octopus
. Приведём greet.go
к следующему виду:package greet
import "fmt"
var Shark = "Sammy"
type Octopus struct {
Name string
Color string
}
func (o Octopus) String() string {
return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}
func Hello() {
fmt.Println("Hello, World!")
}
Теперь откройте
main.go
, создайте в нём экземпляр структуры нового типа и обратитесь к его методу String()
:package main
import (
"fmt"
"github.com/gopherguides/greet"
)
func main() {
greet.Hello()
fmt.Println(greet.Shark)
oct := greet.Octopus{
Name: "Jesse",
Color: "orange",
}
fmt.Println(oct.String())
}
После того, как вы, с помощью конструкции, которая выглядит как
oct := greet.Octopus
, создали экземпляр Octopus
, вы можете обращаться к методам и свойствам типа из пространства имён файла main.go
. Это, в частности, позволяет воспользоваться командой oct.String()
, расположенной в конце файла main.go
, не обращаясь к greet
. Кроме того, мы можем, например, обратиться к полю структуры Color
, воспользовавшись конструкцией oct.Color
. При этом мы, как и тогда, когда вызывали метод, не обращаемся к greet
.Метод
String
типа Octopus
использует функцию fmt.Sprintf
для формирования предложения и возвращает, с помощью return
, результат, строку, в место вызова метода (в данном случае это место находится в main.go
).Запустим программу снова:
go run main.go
Она выведет в консоль следующее:
Hello, World!
Sammy
The octopus's name is "Jesse" and is the color orange.
Теперь, когда мы оснастили Octopus методом
String
, мы получили механизм вывода сведений о типе, подходящий для многократного использования. Если в будущем понадобится изменить поведение этого метода, который может использоваться во многих проектах, достаточно будет один раз отредактировать его код в greet.go
.Экспорт сущностей
Возможно, вы обратили внимание на то, что всё, с чем мы работали, обращаясь к пакету
greet
, имеет имена, начинающиеся с прописной буквы. В Go нет модификаторов доступа наподобие public
, private
или protected
, которые есть в других языках. Видимость сущностей для внешних механизмов контролируется тем, с какой буквы, с маленькой или с большой, начинаются их имена. В результате типы, переменные, функции, имена которых начинаются с прописной буквы, доступны за пределами текущего пакета. Код, который виден за пределами пакета, называется экспортированным.Если оснастить тип
Octopus
новым методом с именем reset
, то этот метод можно будет вызывать из пакета greet
, но не из файла main.go
, который находится за пределами пакета greet
. Вот обновлённый вариант greet.go
:package greet
import "fmt"
var Shark = "Sammy"
type Octopus struct {
Name string
Color string
}
func (o Octopus) String() string {
return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}
func (o Octopus) reset() {
o.Name = ""
o.Color = ""
}
func Hello() {
fmt.Println("Hello, World!")
}
Попытаемся вызвать
reset
из файла main.go
:package main
import (
"fmt"
"github.com/gopherguides/greet"
)
func main() {
greet.Hello()
fmt.Println(greet.Shark)
oct := greet.Octopus{
Name: "Jesse",
Color: "orange",
}
fmt.Println(oct.String())
oct.reset()
}
Это приведёт к появлению следующей ошибки компиляции:
oct.reset undefined (cannot refer to unexported field or method greet.Octopus.reset)
Для того чтобы экспортировать метод
reset
типа Octopus
нужно его переименовать, заменив первую букву, строчную r
, на прописную R
. Сделаем это, отредактировав greet.go
:package greet
import "fmt"
var Shark = "Sammy"
type Octopus struct {
Name string
Color string
}
func (o Octopus) String() string {
return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
}
func (o Octopus) Reset() {
o.Name = ""
o.Color = ""
}
func Hello() {
fmt.Println("Hello, World!")
}
Это приведёт к тому, что мы сможем вызывать
Reset
из других пакетов и при этом не сталкиваться с сообщениями об ошибках:package main
import (
"fmt"
"github.com/gopherguides/greet"
)
func main() {
greet.Hello()
fmt.Println(greet.Shark)
oct := greet.Octopus{
Name: "Jesse",
Color: "orange",
}
fmt.Println(oct.String())
oct.Reset()
fmt.Println(oct.String())
}
Запустим программу:
go run main.go
Вот что попадёт в консоль:
Hello, World!
Sammy
The octopus's name is "Jesse" and is the color orange
The octopus's name is "" and is the color .
Вызвав метод
Reset
, мы очистили поля Name
и Color
нашего экземпляра Octopus
. В результате, при вызове String
, там, где раньше выводилось содержимое полей Name
и Color
, теперь не выводится ничего.Итоги
Написание пакетов Go ничем не отличается от написания обычного Go-кода. Однако размещение кода пакетов в собственных директориях позволяет изолировать код, которым можно воспользоваться в любых других Go-проектах. Здесь мы поговорили о том, как объявлять в пакетах функции, переменные и типы, рассмотрели порядок использования этих сущностей за пределами пакетов, разобрались с тем, где нужно хранить пакеты, рассчитанные на их многократное использование.
Уважаемые читатели! Какие программы вы обычно пишете на Go? Пользуетесь ли вы в них пакетами собственной разработки?
![](https://habrastorage.org/webt/it/t5/3p/itt53pns2iucwylb3bwn1fmmtnu.png)