5. Standard Library

Everyday batteries: time, strings/bytes, files/JSON, HTTP servers/clients, and paths.

Question: How do you correctly format a date and time in Go, and why is the layout string unusual?

Answer: You use the time.Format method with a specific reference layout string: Mon Jan 2 15:04:05 MST 2006. This layout tells the Format function how to format the time by providing an example of the desired output.

Explanation: Unlike other languages that use abstract placeholders (YYYY-MM-DD), Go uses this specific reference time as a mnemonic. The components correspond to Month Day Hour Minute Second Timezone Year. You arrange these components in the format you want. For example, to get 2006-01-02, you would use the layout string "2006-01-02".

now := time.Now()
fmt.Println(now.Format("2006-01-02")) // ISO-like date
fmt.Println(now.Format(time.RFC3339))  // 2006-01-02T15:04:05Z07:00

Question: What are some common and useful functions in the strings package?

Answer: The strings package provides many essential functions for string manipulation, including strings.TrimSpace to remove whitespace, strings.Split to divide a string into a slice, strings.Join to concatenate a slice into a single string, strings.ReplaceAll for search-and-replace, and strings.HasPrefix/strings.HasSuffix to check for substrings at the beginning or end of a string.

Explanation: Becoming familiar with the strings and bytes packages is crucial for a Go developer. They provide optimized and convenient routines that prevent you from reinventing the wheel for common text processing tasks.

Question: How do you read the contents of a file?

Answer: For simple cases where you need the entire file content at once, use os.ReadFile. For larger files or when you need to process a file line-by-line, a more memory-efficient approach is to use os.Open combined with a bufio.NewScanner.

Explanation: os.ReadFile is convenient but can consume a lot of memory if the file is very large. The bufio.Scanner approach is more robust as it reads the file in chunks, making it suitable for streaming data and handling large files without running out of memory.

Question: How do you marshal and unmarshal JSON using struct tags?

Answer: Use the encoding/json package. Fields must be exported to be marshaled. Tags control names and omission.

Explanation: Tags like json:"name,omitempty" rename fields and omit zero values. Unexported (lowercase) fields are ignored. nil slices marshal to null, empty slices to [].

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name,omitempty"`
}

b, _ := json.Marshal(User{ID: 1})
var u User
_ = json.Unmarshal(b, &u)

Question: How do you start a simple HTTP server?

Answer: Use net/http with a handler and http.ListenAndServe.

Explanation: Handlers take (w http.ResponseWriter, r *http.Request). For graceful shutdown, use http.Server with a context.

http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("pong"))
})
log.Fatal(http.ListenAndServe(":8080", nil))

Question: How do you gracefully shut down an HTTP server?

Answer: Use http.Server and call Shutdown(ctx) on signal. This drains existing connections.

Explanation: Combine with context.WithTimeout and OS signal handling.

srv := &http.Server{Addr: ":8080"}
go func() { _ = srv.ListenAndServe() }()
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt)
<-sig
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = srv.Shutdown(ctx)

Question: When to use path/filepath vs path?

Answer: Use path/filepath for OS-native filesystem paths; use path for slash-separated (URL-like) paths.

Question: How do you use the new structured logger log/slog (Go 1.21+)?

Answer: Create a logger with a handler and log key-value pairs.

logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
logger.Info("started", "version", "1.0.0")

Question: How do you set timeouts and connection limits for HTTP clients?

Answer: Set http.Client{Timeout: ...} and customize http.Transport for pools and per-dial timeouts.

Explanation: Reasonable defaults prevent hangs and manage resources.

client := &http.Client{
    Timeout: 10 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:          100,
        IdleConnTimeout:       90 * time.Second,
        TLSHandshakeTimeout:   10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    },
}

Question: How do you read environment variables safely?

Answer: Use os.LookupEnv to distinguish unset from empty values.

Explanation: os.Getenv returns an empty string for both unset and empty; LookupEnv returns (value, found).

Question: How do you parse command-line flags?

Answer: Use the flag package to declare and parse flags in main().

Explanation: Define flags as pointers and call flag.Parse().

port := flag.Int("port", 8080, "listen port")
flag.Parse()
log.Printf("listening on :%d", *port)