How do I Check if a string contains a substring?
if strings.Contains(s, "needle") {
// ...
}
Task-oriented index. Each recipe is a real question paired with the smallest Go that answers it, and a link to the package it comes from. Use ⌘K to jump to one by name.
if strings.Contains(s, "needle") {
// ...
}
For whitespace use strings.Fields.
parts := strings.Split("a,b,c", ",")
// []string{"a", "b", "c"}
s := strings.Join([]string{"a", "b", "c"}, ", ")
// "a, b, c"
s = strings.TrimSpace(s)
strings.Builder avoids the O(n²) of repeated `s += ...`.
var b strings.Builder
for i := 0; i < 1000; i++ {
fmt.Fprintf(&b, "row=%d ", i)
}
result := b.String()
n, err := strconv.Atoi("42")
if err != nil {
return fmt.Errorf("parse count: %w", err)
}
s := strconv.Itoa(42)
// "42"
fmt.Sprintf("%.2f", 3.14159) // "3.14"
Fine for small files. For huge files, stream with os.Open + bufio.
data, err := os.ReadFile("config.yaml")
if err != nil {
return err
}
err := os.WriteFile("out.txt", data, 0644)
f, err := os.Open("data.txt")
if err != nil {
return err
}
defer f.Close()
sc := bufio.NewScanner(f)
for sc.Scan() {
line := sc.Text()
process(line)
}
if err := sc.Err(); err != nil {
return err
}
_, err := os.Stat(path)
switch {
case err == nil:
// exists
case errors.Is(err, fs.ErrNotExist):
// does not exist
default:
return err // some other error (permissions, I/O)
}
err := filepath.WalkDir("dir", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() && strings.HasSuffix(path, ".go") {
fmt.Println(path)
}
return nil
})
f, err := os.CreateTemp("", "myapp-*.json")
if err != nil {
return err
}
defer os.Remove(f.Name())
defer f.Close()
dir, err := os.MkdirTemp("", "myapp-*")
defer os.RemoveAll(dir)
_, err := io.Copy(dst, src)
data, err := io.ReadAll(os.Stdin)
http.DefaultClient has no timeout. For anything outside scripts, build your own &http.Client{Timeout: ...}.
resp, err := http.Get("https://example.com")
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
body, _ := json.Marshal(payload)
resp, err := http.Post(url, "application/json", bytes.NewReader(body))
if err != nil {
return err
}
defer resp.Body.Close()
client := &http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := client.Do(req)
Path patterns and PathValue are Go 1.22+.
mux := http.NewServeMux()
mux.HandleFunc("GET /hello/{name}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello, %s\n", r.PathValue("name"))
})
log.Fatal(http.ListenAndServe(":8080", mux))
u, err := url.Parse("https://api.x/?id=42&since=2026")
if err != nil {
return err
}
id := u.Query().Get("id")
type User struct {
Name string `json:"name"`
Age int `json:"age,omitempty"`
}
b, err := json.Marshal(User{Name: "Ada", Age: 36})
// {"name":"Ada","age":36}
b, err := json.MarshalIndent(v, "", " ")
var u User
if err := json.Unmarshal(data, &u); err != nil {
return err
}
var v any
if err := json.Unmarshal(data, &v); err != nil {
return err
}
// v is map[string]any / []any / string / float64 / bool / nil
Avoids loading the whole body into memory before parsing.
defer resp.Body.Close()
var u User
if err := json.NewDecoder(resp.Body).Decode(&u); err != nil {
return err
}
time.Sleep(2 * time.Second)
start := time.Now()
doWork()
fmt.Println("took", time.Since(start))
s := time.Now().Format(time.RFC3339)
t, err := time.Parse(time.RFC3339, s)
tk := time.NewTicker(5 * time.Second)
defer tk.Stop()
for {
select {
case <-tk.C:
beat()
case <-ctx.Done():
return
}
}
Capture loop variables explicitly. (In Go 1.22+ this is no longer required, but explicit is still clearer.)
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
go func(it Item) {
defer wg.Done()
process(it)
}(item)
}
wg.Wait()
ctx, cancel := context.WithCancel(parent)
defer cancel()
go func() {
if userPressedStop() {
cancel()
}
}()
select {
case result := <-doWork(ctx):
use(result)
case <-ctx.Done():
return ctx.Err()
}
ctx, cancel := context.WithTimeout(parent, 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)
sem := make(chan struct{}, 8) // 8 in flight at once
for _, job := range jobs {
sem <- struct{}{}
go func(j Job) {
defer func() { <-sem }()
run(j)
}(job)
}
var (
once sync.Once
db *sql.DB
)
func getDB() *sql.DB {
once.Do(func() { db = openDB() })
return db
}
slices.Sort(nums)
slices.SortFunc(users, func(a, b User) int {
return cmp.Compare(a.Age, b.Age)
})
Compact removes adjacent duplicates only; sort first.
slices.Sort(s)
s = slices.Compact(s)
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
slices.Sort(keys)
Go 1.23+ — uses iter.Seq from maps.Keys.
for _, k := range slices.Sorted(maps.Keys(m)) {
fmt.Println(k, m[k])
}
if err := load(id); err != nil {
return fmt.Errorf("load user %s: %w", id, err)
}
var ErrNoUser = errors.New("no user")
if errors.Is(err, ErrNoUser) {
// 404 path
}
var pe *fs.PathError
if errors.As(err, &pe) {
log.Printf("path %q failed: %v", pe.Path, pe.Err)
}
port := flag.Int("port", 8080, "listen port")
verbose := flag.Bool("v", false, "verbose")
flag.Parse()
fmt.Println(*port, *verbose, flag.Args())
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
// or, Go 1.22+:
port = cmp.Or(os.Getenv("PORT"), "8080")
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
// pass ctx into your work; <-ctx.Done() fires on ^C
os.Exit skips deferred functions; prefer returning errors from main.
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
sum := sha256.Sum256([]byte("hello"))
hex := hex.EncodeToString(sum[:])
crypto/rand, never math/rand, for anything that protects secrets.
buf := make([]byte, 32)
if _, err := rand.Read(buf); err != nil {
return err
}
Go 1.22+ math/rand/v2 — auto-seeded, no Seed call needed.
n := rand.IntN(100) // 0..99
func TestAdd(t *testing.T) {
got := add(1, 2)
if got != 3 {
t.Fatalf("add(1,2) = %d, want 3", got)
}
}
func TestAdd(t *testing.T) {
cases := []struct {
name string
a, b int
want int
}{
{"zero", 0, 0, 0},
{"pos", 2, 3, 5},
{"neg", -1, 1, 0},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := add(c.a, c.b); got != c.want {
t.Fatalf("add(%d,%d) = %d, want %d", c.a, c.b, got, c.want)
}
})
}
}
Default handler writes text. For JSON: slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr, nil))).
slog.Info("request",
"method", r.Method,
"path", r.URL.Path,
"status", code,
"ms", elapsed.Milliseconds(),
)