Modern Go Engineering
Chapitre 3
03 - Go Standard Library
03 - Go Standard Library
Cours 03 : Go Standard Library
1. Package fmt
1.1 Formatage
// Verbes principaux
fmt.Printf("%v\n", 42) // valeur par défaut
fmt.Printf("%+v\n", user) // avec noms de champs
fmt.Printf("%#v\n", user) // représentation Go
fmt.Printf("%T\n", user) // type
// Types spécifiques
fmt.Printf("%d\n", 42) // entier décimal
fmt.Printf("%x\n", 255) // hexadécimal
fmt.Printf("%f\n", 3.14) // flottant
fmt.Printf("%.2f\n", 3.14159) // précision
fmt.Printf("%s\n", "hello") // string
fmt.Printf("%q\n", "hello") // string quotée
fmt.Printf("%08b\n", 5) // binaire avec padding
1.2 Fprintf, Sprintf, Fscanf
// Écriture dans un writer
fmt.Fprintf(os.Stdout, "Hello %s\n", "world")
// Formatage en string
s := fmt.Sprintf("Value: %d", 42)
// Lecture formatée
var name string
var age int
fmt.Sscanf("Alice 30", "%s %d", &name, &age)
2. Package io
2.1 Reader et Writer
// Interface Reader
type Reader interface {
Read(p []byte) (n int, err error)
}
// Interface Writer
type Writer interface {
Write(p []byte) (n int, err error)
}
// Implémentation avec strings.Reader
r := strings.NewReader("Hello, World!")
buf := make([]byte, 5)
n, err := r.Read(buf)
fmt.Printf("Lu %d bytes: %s\n", n, buf[:n])
2.2 io.Copy
// Copie de reader vers writer
resp, _ := http.Get("https://example.com")
defer resp.Body.Close()
// Copie directe vers stdout
io.Copy(os.Stdout, resp.Body)
// Copie avec buffer
io.CopyBuffer(os.Stdout, resp.Body, make([]byte, 4096))
// Copie avec limite
io.CopyN(os.Stdout, resp.Body, 1024)
2.3 bufio
// Lecture bufferisée
f, _ := os.Open("file.txt")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
// Écriture bufferisée
w := bufio.NewWriter(os.Stdout)
w.WriteString("Hello, ")
w.WriteString("World!\n")
w.Flush() // Important : vider le buffer
3. Package strings et strconv
3.1 strings
s := "Hello, World!"
strings.Contains(s, "World") // true
strings.HasPrefix(s, "Hello") // true
strings.HasSuffix(s, "!") // true
strings.Index(s, "World") // 7
strings.Split(s, ", ") // ["Hello", "World!"]
strings.Join([]string{"a", "b"}, ",") // "a,b"
strings.ReplaceAll(s, "World", "Go") // "Hello, Go!"
strings.ToUpper(s) // "HELLO, WORLD!"
strings.TrimSpace(" hello ") // "hello"
strings.Fields("a b c") // ["a", "b", "c"]
// Builder efficace
var b strings.Builder
b.WriteString("Hello")
b.WriteByte(' ')
b.WriteString("World")
fmt.Println(b.String())
3.2 strconv
// String ↔ int
i, _ := strconv.Atoi("42") // 42
s := strconv.Itoa(42) // "42"
// Parse
f, _ := strconv.ParseFloat("3.14", 64) // 3.14
b, _ := strconv.ParseBool("true") // true
i, _ := strconv.ParseInt("FF", 16, 64) // 255
// Format
s := strconv.FormatInt(255, 16) // "ff"
s := strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
4. Package time
4.1 Time et Duration
now := time.Now()
fmt.Println(now.Format(time.RFC3339)) // 2024-01-15T10:30:00+01:00
// Duration
d := 5 * time.Second + 500 * time.Millisecond
fmt.Println(d) // 5.5s
fmt.Println(d.Seconds()) // 5.5
fmt.Println(d.Milliseconds()) // 5500
// Parsing
t, _ := time.Parse("2006-01-02", "2024-01-15")
// Calculs
tomorrow := now.Add(24 * time.Hour)
diff := tomorrow.Sub(now)
fmt.Printf("%.0f heures\n", diff.Hours())
4.2 Ticker et Timer
// Timer (une fois)
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("Timer expired")
// Ticker (périodique)
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
go func() {
for {
select {
case t := <-ticker.C:
fmt.Println("Tick at", t)
case <-done:
return
}
}
}()
time.Sleep(2 * time.Second)
ticker.Stop()
done <- true
// After (simple)
select {
case <-time.After(1 * time.Second):
fmt.Println("timeout")
case result := <-ch:
fmt.Println(result)
}
5. Package net/http
5.1 Client HTTP
client := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
},
}
// GET
resp, err := client.Get("https://api.example.com/users")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Status: %d, Body: %s\n", resp.StatusCode, body)
// POST avec JSON
data := map[string]any{"name": "Alice"}
jsonData, _ := json.Marshal(data)
resp, err = client.Post(
"https://api.example.com/users",
"application/json",
bytes.NewReader(jsonData),
)
5.2 RoundTripper et Middleware
type LoggingTransport struct {
Transport http.RoundTripper
}
func (t *LoggingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
start := time.Now()
log.Printf("Request: %s %s", req.Method, req.URL)
resp, err := t.Transport.RoundTrip(req)
log.Printf("Response: %d in %v", resp.StatusCode, time.Since(start))
return resp, err
}
client := &http.Client{
Transport: &LoggingTransport{
Transport: http.DefaultTransport,
},
}
6. Package encoding/json
6.1 Marshal/Unmarshal
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"`
CreatedAt time.Time `json:"created_at"`
Password string `json:"-"`
}
// Marshal
user := User{ID: 1, Name: "Alice", CreatedAt: time.Now()}
data, _ := json.Marshal(user)
fmt.Println(string(data))
data, _ = json.MarshalIndent(user, "", " ")
fmt.Println(string(data))
// Unmarshal
jsonData := `{"id":1,"name":"Alice","email":"alice@example.com"}`
var u User
json.Unmarshal([]byte(jsonData), &u)
6.2 Encoder/Decoder
// Encoder vers writer
f, _ := os.Create("users.json")
defer f.Close()
encoder := json.NewEncoder(f)
encoder.SetIndent("", " ")
encoder.Encode(user)
// Decoder depuis reader
f, _ = os.Open("users.json")
defer f.Close()
decoder := json.NewDecoder(f)
var users []User
decoder.Decode(&users)
6.3 Interfaces et JSON
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64 `json:"radius"`
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// Désérialisation polymorphique
type ShapeWrapper struct {
Type string `json:"type"`
Config json.RawMessage `json:"config"`
}
func decodeShape(raw json.RawMessage, shapeType string) (Shape, error) {
switch shapeType {
case "circle":
var c Circle
json.Unmarshal(raw, &c)
return c, nil
default:
return nil, fmt.Errorf("unknown shape: %s", shapeType)
}
}
7. Package context
7.1 Création et propagation
type contextKey string
const (
TraceIDKey contextKey = "trace_id"
UserIDKey contextKey = "user_id"
)
func WithTraceID(ctx context.Context, traceID string) context.Context {
return context.WithValue(ctx, TraceIDKey, traceID)
}
func GetTraceID(ctx context.Context) (string, bool) {
traceID, ok := ctx.Value(TraceIDKey).(string)
return traceID, ok
}
func handler(ctx context.Context) {
ctx = WithTraceID(ctx, uuid.New().String())
// Propagation
result, err := databaseQuery(ctx)
if err != nil {
log.Printf("trace=%s: %v", GetTraceID(ctx), err)
}
}
7.2 Deadlines et annulation
func fetchWithRetry(ctx context.Context, url string) (*http.Response, error) {
var lastErr error
for i := 0; i < 3; i++ {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)
if err == nil {
return resp, nil
}
lastErr = err
time.Sleep(time.Duration(math.Pow(2, float64(i))) * 100 * time.Millisecond)
}
return nil, fmt.Errorf("all retries failed: %w", lastErr)
}
8. Package reflect
8.1 Type et Value
func inspect(v any) {
t := reflect.TypeOf(v)
val := reflect.ValueOf(v)
fmt.Printf("Type: %s, Kind: %s\n", t, t.Kind())
// Itération sur les champs d'une struct
if t.Kind() == reflect.Struct {
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fval := val.Field(i)
fmt.Printf(" Field %d: %s (%s) = %v [tag: %s]\n",
i, field.Name, field.Type, fval.Interface(), field.Tag)
}
}
}
type Config struct {
Host string `json:"host" default:"localhost"`
Port int `json:"port" default:"8080"`
}
func main() {
inspect(Config{Host: "example.com"})
}
8.2 Création dynamique
func populateDefaults(v any) {
val := reflect.ValueOf(v)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
t := val.Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fval := val.Field(i)
if defaultVal, ok := field.Tag.Lookup("default"); ok {
if fval.IsZero() {
switch fval.Kind() {
case reflect.String:
fval.SetString(defaultVal)
case reflect.Int:
n, _ := strconv.Atoi(defaultVal)
fval.SetInt(int64(n))
}
}
}
}
}
8.3 Limitations
// reflect ne peut pas :
// - Créer des types génériques
// - Accéder aux variables non exportées
// - Être utilisé avec des génériques simplement
// - Garantir la performance (10-100x plus lent)
// Préférer :
// - gob/encoding pour la sérialisation
// - code generation (sqlc, ogen)
// - interfaces pour le polymorphisme
9. Package sort, container, math, crypto
9.1 sort
ints := []int{5, 2, 8, 1, 9}
sort.Ints(ints)
strings := []string{"banana", "apple", "cherry"}
sort.Strings(strings)
// Custom sort
type ByAge []User
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
sort.Sort(ByAge(users))
sort.Slice(users, func(i, j int) bool {
return users[i].Age < users[j].Age
})
9.2 container
// heap
h := &IntHeap{2, 1, 5}
heap.Init(h)
heap.Push(h, 3)
fmt.Println(heap.Pop(h)) // 1
// list (double chaînée)
l := list.New()
l.PushBack("a")
l.PushFront("b")
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
// ring (circulaire)
r := ring.New(3)
for i := 0; i < r.Len(); i++ {
r.Value = i
r = r.Next()
}
9.3 math
math.Max(10, 20)
math.Min(10, 20)
math.Abs(-10)
math.Sqrt(16)
math.Pow(2, 10)
math.Round(3.14159)
math.Floor(3.9)
math.Ceil(3.1)
math.Sin(math.Pi / 2)
math.Mod(10, 3)
9.4 crypto
// SHA-256
hash := sha256.Sum256([]byte("hello"))
fmt.Printf("%x\n", hash)
// HMAC
key := []byte("secret-key")
mac := hmac.New(sha256.New, key)
mac.Write([]byte("message"))
signature := mac.Sum(nil)
// AES
block, _ := aes.NewCipher([]byte("key-32-bytes-long"))
ciphertext := make([]byte, len(plaintext))
block.Encrypt(ciphertext, plaintext)
// Random sécurisé
token := make([]byte, 32)
rand.Read(token)
fmt.Printf("Token: %x\n", token)
10. Package encoding (XML, CSV)
10.1 XML
type Person struct {
XMLName xml.Name `xml:"person"`
ID int `xml:"id,attr"`
Name string `xml:"name"`
Age int `xml:"age"`
}
data := `<person id="1"><name>Alice</name><age>30</age></person>`
var p Person
xml.Unmarshal([]byte(data), &p)
10.2 CSV
f, _ := os.Create("data.csv")
w := csv.NewWriter(f)
w.Write([]string{"name", "age", "email"})
w.Write([]string{"Alice", "30", "alice@example.com"})
w.Write([]string{"Bob", "25", "bob@example.com"})
w.Flush()
f, _ = os.Open("data.csv")
r := csv.NewReader(f)
records, _ := r.ReadAll()
for _, record := range records {
fmt.Println(record)
}
Résumé
- fmt : formatage, Fprintf/Sscanf
- io : Reader/Writer, Copy, bufio
- strings/strconv : manipulation de strings
- time : Time, Duration, Ticker, Timer
- net/http : Client, Transport, middleware
- encoding/json : Marshal/Unmarshal, tags, Encoder/Decoder
- context : propagation, annulation, valeurs
- reflect : introspection (usage modéré)
- sort/container/math/crypto : utilitaires