Go: импорт и видимость

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: импорт и видимость

Непрочитанное сообщение Olej » 19 фев 2024, 16:33

Первоначально (до версии 1.11, а это не так давно) в Go были только пакеты. И правила импорта и видимости относились только к пакетам.
Потом появились модули. И, кроме версионности, модули повлияли на правила импортирования и видимости.
И возникает некоторая путаница... по крайней мере у меня. Нужно уточняться.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: импорт и видимость

Непрочитанное сообщение Olej » 19 фев 2024, 16:37

Olej писал(а):
19 фев 2024, 16:33
Первоначально (до версии 1.11, а это не так давно) в Go были только пакеты.
Так это было:
Как писать Go-пакеты
27 авг 2019 в 12:47
Пакет Go состоит из Go-файлов, расположенных в одной и той же директории, в начале которых имеется одинаковое выражение package. Пакеты, подключаемые к программам, позволяют расширять их возможности.
Пакеты и модули
Последнее обновление: 24.12.2017
Есть два типа пакетов: исполняемые (executable) и библиотеки (reusable). Для создания исполняемых файлов пакет должен иметь имя main. Все остальные пакеты не являются исполняемыми. При этом пакет main должен содержать функцию main, которая является входной точкой в приложение.
А так это стало:
Определение модуля, файл go.mod
суббота, 6 июля 2019 г.
Модуль определяется деревом исходных файлов Go с файлом go.mod в корневом каталоге дерева. Каталог, содержащий файл go.mod, называется корнем модулем (module root). Обычно корень модуля также будет соответствовать корню хранилища исходного кода (но в общем случае это не обязательно). Модуль представляет собой набор всех пакетов Go в корневом каталоге модуля и его подкаталогах, но исключая поддеревья с собственными файлами go.mod.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: импорт и видимость

Непрочитанное сообщение Olej » 19 фев 2024, 16:54

Olej писал(а):
19 фев 2024, 16:37
При этом пакет main должен содержать функцию main, которая является входной точкой в приложение.
Это вот всё в одном пакете:
- test1.go

Код: Выделить всё

package main

import "fmt"

func demo() {
    fmt.Println("HI")
}
- test2.go

Код: Выделить всё

package main

func main() {
    demo()
}
Сборка:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/private$ go build *.go

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/private$ ls -l
итого 1860
-rwxrwxr-x 1 olej olej 1895359 фев 19 12:28 test1
-rw-rw-r-- 1 olej olej      66 фев 19 12:27 test1.go
-rw-rw-r-- 1 olej olej      41 фев 19 12:27 test2.go

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/private$ ./test1
HI
Что произошло? 2 .go объединяются в один пакет, видимость внутри даже имён начинающихся с малых букв, старт идёт из кода test2.go, хотя исполнимыё файл создаётся test1...
А вот так будет наоборот (хотя это и не имеет значения):

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/private$ go build test2.go test1.go

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/private$ ls -o
итого 1864
-rw-rw-r-- 1 olej    1514 фев 19 12:31 private.hist
-rw-rw-r-- 1 olej      66 фев 19 12:27 test1.go
-rwxrwxr-x 1 olej 1895351 фев 19 12:34 test2
-rw-rw-r-- 1 olej      41 фев 19 12:27 test2.go
И размеры исполнимого ELF-файла слегка раазличаются...
Наконец, можно так:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/private$ go run test2.go test1.go
HI
Или даже просто вот так:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/private$ go run .
HI
Вложения
private.tgz
(669 байт) 5 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: импорт и видимость

Непрочитанное сообщение Olej » 19 фев 2024, 17:18

Olej писал(а):
19 фев 2024, 16:54
Это вот всё в одном пакете:
Теперь хочу разнести пакет вызывающий и пакет библиотечный... И для библиотеки создаю отдельный каталог lib... Но сослаться на него в import вызывающего - нельзя, получите сообщение типа такого (то что имя пакета/каталога там названо ./digree не имеет значения ... так было):

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/packages$ go build *.go
head.go:8:5: "./digree" is relative, but relative import paths are not supported in module mode
found packages degree (digree.go) and main (head.go) in /home/olej/2024/own.BOOKs/BHV.Go.3/examples.work/packages
Поэтому извсего дерева каталогов гланого пакете main создаю модуль:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/public$ go mod init public
go: creating new go.mod: module public
go: to add module requirements and sums:
    go mod tidy
Имя public - совершенно произвольное :!: Это только указание на то место где когда-то модуль может размещаться, возможно в GIT-репоитории в Интернет ... про это целая тема: Go: модули

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/public$ cat go.mod
module public

go 1.22.0
Фактически, это ненужный, в нашем случае, файл (манифест модуля его иногда называют), но он вводит имя модуля, на которое можно ссылаться в import:
- test2.go:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/public$ cat test2.go
package main

import "public/lib"

func main() {
    lib1.Demo()
}
И теперь можно сделать и библиотечный пакет:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/public$ cat lib/test1.go
package lib1

import "fmt"

func Demo() {
    fmt.Println("HI")
}
Имя функции для импорта, видимое за границей пакета, должно начинаться теперь с большой буквы (это мы помним из правил Go) :!:
Структура проекта теперь такая:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/public$ tree
.
├── go.mod
├── lib
│   └── test1.go
├── public.hist
└── test2.go

1 directory, 4 files
И поехали :lol: :

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/public$ go run test2.go
HI

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/public$ go run .
HI
Супер :!: :-D
Но :-o ... Стоп :!: :roll: :-o
- import в вызывающем test2.go ссылается на "public/lib"
- а пакет в lib/test1.go назван как package lib1 :-o (специально)
- т.е. импорту теперь (при наличии модулей) пофиг имена пакетов :?:
Вложения
public.tgz
(788 байт) 5 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: импорт и видимость

Непрочитанное сообщение Olej » 19 фев 2024, 17:24

Olej писал(а):
19 фев 2024, 17:18
Теперь хочу разнести
Небольшое дополнение... Импорту можно присвоить алиас, синоним...
Как-то нигде этого не усмотрел в документации, но ... подсмотрел у кого-то в кодах :lol: :
Меняю только вызывающий test2.go:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/public.ali$ cat test2.go 
package main

import (
    L "public/lib"
)
//import L "public/lib"

func main() {
    L.Demo()
}
Т.е. импортируемому имени (может неудобному и длинному) присваивается любой короткий синоним так. (Как одна запись, так и другая, закомментированная - работают.)
Поехали:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/public.ali$ go run .
HI

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/types/looking.for/public.ali$ go run test2.go
HI
Синтаксически всё безукоризненно :!:
Вложения
public.ali.tgz
(793 байт) 5 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: импорт и видимость

Непрочитанное сообщение Olej » 19 фев 2024, 18:50

Olej писал(а):
19 фев 2024, 17:18
Теперь хочу разнести пакет вызывающий и пакет библиотечный...
Пример куда интереснее ... головной модуль - вычисляет тригонометрические функции, но когда аргумент в градусах - передаёт одному пакету, а когда в радианах, то другому...
В главном (корневом) каталоге проекта:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ go mod init angels
go: creating new go.mod: module angels
go: to add module requirements and sums:
    go mod tidy

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ cat go.mod
module angels

go 1.22.0
Тривиально... обозвали как-то модуль.
После этого структура каталогов/файлов становится такой:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ tree
.
├── angels.hist
├── digree
│   └── углы.go
├── go.mod
├── head.go
└── radian
    └── рад1.go

2 directories, 5 files
Головной пакет:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ cat head.go 
package main

import (
	"fmt"
	"math"
)

import "angels/radian"
import "angels/digree"

func main() {
	const угол1, угол2 = 30., 30. / 180 * math.Pi
	fmt.Printf("синус %f градусов = %f\n", угол1, углы.Sin(угол1))
	fmt.Printf("синус %f радиан   = %f\n", угол2, радианы.Sin(угол2))
}
Обращаем внимание: в import указаны каталоги модуля для вычисляющих пакетов :!:
Имена каталогов (импорта) не совпадают ни с именами реализующих .go файлов, ни с именами пакетов названных в этих .go файлах исходников :!:
2 отдельных каталога с отдельными пакетами...
Это пакет "радианы":

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ ls radian/
рад1.go

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ cat radian/рад1.go
package радианы

import "math"

func Cos(rd float64) float64 {
    return math.Cos(rd)
}

func Sin(rd float64) float64 {
    return math.Sin(rd)
}
И, ещё веселее, пакет "углы":

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ ls digree/
углы.go

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ cat digree/углы.go
package углы

import "math"

var a2r func(dg float64) float64

func Cos(dg float64) float64 {
    return math.Cos(a2r(dg))
}

func Sin(dg float64) float64 {
    return math.Sin(a2r(dg))
}

func init() {
    a2r = func(dg float64) float64 {
        return dg / 180 * math.Pi
    }
}
И именно по этим именам пакетов идёт квалифицированный вызов в головном main() !

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ go run head.go
синус 30.000000 градусов = 0.500000
синус 0.523599 радиан   = 0.500000
Или так:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels$ go run .
синус 30.000000 градусов = 0.500000
синус 0.523599 радиан   = 0.500000
Вложения
angels.tgz
(1.29 КБ) 5 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: импорт и видимость

Непрочитанное сообщение Olej » 19 фев 2024, 19:14

Olej писал(а):
19 фев 2024, 18:50
2 отдельных каталога с отдельными пакетами...
Я попробовал 2 разных пакета (их файлы) разместить в одном общем каталоге (назвал его trigonom)...
Olej писал(а):
19 фев 2024, 16:37
Пакет Go состоит из Go-файлов, расположенных в одной и той же директории, в начале которых имеется одинаковое выражение package.
И при сборке (или сразу при запуске) получаем вот такую ошибку:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels2$ go run .
head.go:10:8: found packages радианы (рад1.go) and углы (углы.go) in /home/olej/2024/own.BOOKs/BHV.Go.3/examples.work/looking.for/angels2/trigonom
Вот так :!:
И тогда определение пакета, пожалуй, стоит переформулировать - как-то так:
- все *.go файлы пакета расположенных в одной и той же директории...
- и каждый отдельный пакет в своей собственной директории...
Имеет ли смысл иметь два пакета в одном каталоге?
У вас не может быть двух пакетов в каталоге, поэтому ошибка.

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 10 гостей