How are contexts structured in the Go language?
sobes.tech AI
Answer from AI
The (context.Context) in Go is used to pass information about deadlines, cancellations, and other data across API boundaries. It forms a tree: each context is a child of another, except for the root (context.Background() or context.TODO()).
Main methods of the context.Context interface:
Deadline() (deadline time.Time, ok bool): Returns the deadline of the context.Done() <-chan struct{}: Returns a channel that is closed when the context is canceled.Err() error: Returns the error that caused the cancellation.Value(key any) any: Returns the value associated with the key in the context.
Created using functions from the context package:
context.Background(): The root, empty context, used as the base for the main process.context.TODO(): Used as a placeholder when the specific context is unknown.context.WithCancel(parent Context): Creates a child context and a cancel function.context.WithDeadline(parent Context, d time.Time): Creates a child context with a deadline.context.WithTimeout(parent Context, timeout time.Duration): Creates a child context with a timeout.context.WithValue(parent Context, key, val any): Creates a child context with a value.
Main implementations of Context:
emptyCtx: Basic, non-cancelable, without values. Used byBackgroundandTODO.cancelCtx: Supports cancellation. Consists of a base context and a cancel channel.timerCtx: Supports deadline/timeout. ContainscancelCtxand a timer.valueCtx: Supports passing values. Contains a base context and key-value pairs.
When a parent context is canceled, all child contexts are automatically canceled. This allows propagation of cancel signals or deadlines through the call chain.
Example of using context.WithCancel:
package main
import (
"context"
"fmt"
"time"
)
func main() {
// Create a root context and a cancel function
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Ensure cancellation on main exit
go func() {
select {
case <-ctx.Done(): // Wait for context cancellation
fmt.Println("Task canceled:", ctx.Err())
return
case <-time.After(5 * time.Second): // Or finish after 5 seconds
fmt.Println("Task completed")
}
}()
// Cancel the context from another place
time.Sleep(1 * time.Second)
fmt.Println("Canceling context...")
cancel()
// Give the goroutine time to handle cancellation
time.Sleep(100 * time.Millisecond)
}
Example of using context.WithValue:
package main
import (
"context"
"fmt"
)
type userIDKey string
func processRequest(ctx context.Context) {
if userID, ok := ctx.Value(userIDKey("UserID")).(string); ok {
fmt.Println("Processing request for User ID:", userID)
} else {
fmt.Println("User ID not found in context")
}
}
func main() {
ctx := context.WithValue(context.Background(), userIDKey("UserID"), "12345")
processRequest(ctx)
}
Contexts are thread-safe and immutable, which is important for concurrent code. They are not meant for passing raw parameters to functions but rather for meta-information that influences process execution.