Chaque fichier Go appartient à un package, déclaré sur sa première ligne. Le code d'un package en utilise un autre en l'important et en préfixant les noms par le nom du package. Seuls les noms qui commencent par une majuscule peuvent être utilisés hors du package.
fmt, math et strings sont des packages de la bibliothèque standard. Ils sont livrés avec Go, donc en « ajouter » un revient à écrire son nom dans le bloc import. Rien n'est téléchargé. fmt.Println, math.Sqrt et strings.Repeat commencent par une majuscule parce qu'ils sont exportés.
La clause package
La première ligne qui n'est pas un commentaire dans chaque fichier .go est package name. Tous les fichiers d'un répertoire doivent utiliser le même nom, et ensemble ils forment un package. Un répertoire avec deux noms de package différents ne compile pas :
found packages greeting (b.go) and greet (greet.go) in /home/ana/shop/greet
(La seule exception concerne les fichiers de test, qui peuvent utiliser package name_test.)
package main est spécial : il produit un exécutable, et le programme démarre à sa func main(). Tous les autres packages sont des bibliothèques destinées à être importées.
Par convention, le nom du package correspond au dernier élément du chemin du répertoire : le code de shop/greet est package greet. Les noms de packages sont courts, en minuscules, en un seul mot, sans underscores ni majuscules internes : strconv, httptest, greet, pas string_utils ni greetHelpers.
Importer des packages
Un chemin d'import est une chaîne. Pour la bibliothèque standard, c'est le chemin du package dans l'arborescence des sources de Go ; pour tout le reste, il commence par un chemin de module :
import (
"fmt" // standard library
"net/http" // standard library, nested path
"math/rand/v2" // standard library, version 2 of math/rand
"github.com/google/uuid" // another module (needs go get)
"example.com/shop/greet" // a package in your own module
)
Le chemin est l'endroit où vit le package ; le nom est ce que vous tapez dans le code. Pour net/http le nom est http, pour math/rand/v2 c'est rand. Vous écrivez http.Get, jamais net/http.Get.
gofmt et goimports trient le bloc et, par convention, séparent les imports de la bibliothèque standard du reste par une ligne vide.
Les imports inutilisés sont une erreur de compilation :
./main.go:5:2: "os" imported and not used
Les éditeurs qui gèrent Go lancent goimports à l'enregistrement, ce qui ajoute les imports standard manquants et retire ceux qui sont inutilisés à votre place.
Noms exportés et non exportés
La visibilité en Go se décide par une seule règle : si un nom commence par une majuscule, il est exporté. Cela s'applique aux fonctions, types, variables, constantes, champs de struct et méthodes. Il n'y a pas de mots-clés public, private ou protected.
| Nom | Visible hors du package ? |
|---|---|
func Hello() | oui |
func hello() | non |
type User struct | oui |
champ User.Name | oui |
champ User.email | non |
const MaxSize | oui |
var defaultTimeout | non |
Essayer d'utiliser un nom non exporté depuis un autre package donne une erreur undefined :
./main.go:11:20: undefined: greet.prefix
La règle compte même à l'intérieur d'un seul fichier, parce que d'autres packages inspectent vos types. encoding/json ne voit que les champs exportés :
token est laissé hors du JSON, sans bruit. C'est une fonctionnalité quand le champ contient des données privées, et une surprise quand vous avez oublié la majuscule d'un champ que vous vouliez.
Répartir un package sur plusieurs fichiers
Un package peut s'étendre sur autant de fichiers que vous voulez. Chaque fichier du répertoire avec la même ligne package voit tous les noms déclarés dans les autres, exportés ou non, sans rien importer :
shop/
├── go.mod (module example.com/shop)
├── main.go
└── math.go
// main.go
package main
import "fmt"
func main() {
fmt.Println(total(2, 3))
}
// math.go
package main
func total(a, b int) int { return a + b }
main.go appelle total directement. Il n'y a pas de #include ni d'import entre les fichiers d'un même package.
L'erreur « undefined » de go run main.go
C'est le problème de package le plus recherché en Go, et il a une seule cause. Lancer uniquement le fichier qui contient main ne compile que ce fichier :
go run main.go
# command-line-arguments
./main.go:6:14: undefined: total
command-line-arguments est le nom que Go donne à un package construit à partir d'une liste de fichiers, et math.go ne fait pas partie de cette liste. Lancez plutôt tout le package :
go run .
5
La même chose vaut pour go build main.go. Utilisez go run . et go build (ou go build ./cmd/app) et le problème n'apparaît jamais. S'il persiste avec go run ., vérifiez que les deux fichiers indiquent package main et qu'aucun n'a de contrainte de build (//go:build) ou de suffixe _test.go qui l'exclut.
Vos propres packages
Un sous-répertoire de votre module est un package séparé. Importez-le avec le chemin du module de go.mod plus le répertoire :
shop/
├── go.mod (module example.com/shop)
├── main.go
└── greet/
└── greet.go
// greet/greet.go
package greet
const prefix = "Hi, "
// Hello returns a greeting for name.
func Hello(name string) string {
return prefix + name
}
// main.go
package main
import (
"fmt"
"example.com/shop/greet"
)
func main() {
fmt.Println(greet.Hello("Ana"))
}
Hi, Ana
Deux choses piègent ici. L'import est le chemin du module plus le répertoire, pas un chemin relatif : "./greet" ne fonctionne pas en mode module. Et prefix reste caché pour main parce qu'il est en minuscule ; seul Hello fait partie de l'API du package.
Packages internal
Un répertoire nommé internal restreint qui peut importer ce qu'il contient. Le code de shop/internal/store peut être importé par tout package enraciné sous shop/, et par rien en dehors. Un autre module qui essaie obtient :
use of internal package example.com/shop/internal/store not allowed
Utilisez internal/ pour du code que vous voulez partager entre vos propres packages sans le promettre au reste du monde. Vous pouvez modifier son API librement, puisque aucun code extérieur ne peut en dépendre.
Alias d'import
Donnez un autre nom local à un import en écrivant le nom avant le chemin. La raison habituelle est deux packages portant le même nom :
Les deux packages s'appellent rand, donc au moins un a besoin d'un alias. Utilisez des alias pour les conflits et pour les noms peu clairs ; ne renommez pas des packages juste pour les raccourcir, car les lecteurs connaissent strconv, pas votre sc.
Imports blancs et imports point
Import blanc (_). Importe un package uniquement pour ses effets de bord : ses variables de package et ses fonctions init s'exécutent, mais vous n'utilisez aucun de ses noms. Les drivers de base de données et les décodeurs d'images s'enregistrent ainsi :
import (
"database/sql"
_ "github.com/lib/pq" // registers the "postgres" driver with database/sql
)
Sans le _, l'import serait une erreur d'import inutilisé.
Import point (.). Place les noms exportés du package directement dans la portée de votre fichier, donc vous écrivez Println au lieu de fmt.Println. Cela masque l'origine des noms et c'est déconseillé en dehors de quelques motifs d'utilitaires de test.
Cycles d'import
Go n'autorise pas deux packages à s'importer mutuellement, directement ou par une chaîne :
package example.com/shop
imports example.com/shop/greet from main.go
imports example.com/shop/other from c.go
imports example.com/shop/greet from o.go: import cycle not allowed
L'erreur liste la chaîne. Deux corrections couvrent presque tous les cas :
- Déplacez le code dont les deux packages ont besoin dans un troisième package de plus bas niveau que les deux importent.
- Si le package A a seulement besoin d'appeler quelque chose dans B, définissez une petite interface dans A et laissez le type de B la satisfaire. A n'importe plus B.
Un cycle signifie généralement que les frontières des packages sont tracées autour de couches de code qui vont en réalité ensemble, c'est donc aussi un signal pour repenser le découpage.
Erreurs courantes
- Lancer un seul fichier.
go run main.godans un package à plusieurs fichiers : utilisezgo run .. - Des imports relatifs.
import "./utils"échoue en mode module. Utilisez"example.com/yourmodule/utils". - Des noms en minuscule que vous vouliez exporter. Une fonction, un champ ou une méthode dont d'autres packages ont besoin doit commencer par une majuscule.
- Un nom de package qui ne correspond pas au répertoire. C'est légal, mais déroutant : le chemin d'import dit
utilset le code dithelpers.X. Gardez-les identiques. - Des noms de packages fourre-tout.
util,commonetmiscne disent rien de leur contenu et ont tendance à devenir des aimants à cycles d'import. Nommez les packages d'après ce qu'ils fournissent :money,auth,slug.
Questions fréquentes
Pourquoi Go dit-il qu'une fonction du même package est undefined ?
Vous avez probablement lancé go run main.go. Nommer un fichier ne compile que ce fichier, donc les fonctions définies dans les autres fichiers du même package manquent : undefined: total. Lancez plutôt go run ., qui compile tous les fichiers .go du répertoire. Vérifiez aussi que chaque fichier a la même ligne package.
Comment ajouter le package fmt en Go ?
fmt fait partie de la bibliothèque standard et est livré avec Go, donc il n'y a rien à installer. Ajoutez import "fmt" après la ligne package et appelez ses fonctions avec le préfixe fmt., par exemple fmt.Println("hi"). Il en va de même pour strings, os, time, net/http et tous les autres packages standard.
Comment importer mon propre package en Go ?
Importez-le par le chemin du module plus le répertoire. Dans un module nommé example.com/shop, un package du dossier greet s'importe avec import "example.com/shop/greet" et s'utilise comme greet.Hello(). Seuls les noms qui commencent par une majuscule sont visibles pour celui qui importe.
Quelle est la différence entre exporté et non exporté en Go ?
Un nom qui commence par une majuscule (Hello, Total, User.Name) est exporté et peut être utilisé depuis d'autres packages. Un nom en minuscule (hello, total, user.name) n'est pas exporté et n'est visible que dans son propre package. Il n'y a pas de mots-clés public ou private.
Comment corriger « import cycle not allowed » en Go ?
Deux packages s'importent mutuellement, directement ou par une chaîne, ce que Go interdit. Déplacez le code partagé dans un troisième package que les deux importent, ou définissez une petite interface dans le package qui a besoin du comportement pour qu'il n'importe plus l'autre.