Go

What are the tradeoffs of using cgo? When would you avoid it?

Question 426HardGo 1.22 to 1.25

"cgo is not Go." The costs:

  • Call overhead: each Go-to-C call switches to the system stack and tells the scheduler. That is tens of nanoseconds, versus about 1 ns for a Go call. Chatty per-element calls kill performance, so batch the work.
  • Threads: a goroutine blocked in C holds an OS thread. Thousands of blocking C calls mean thousands of threads.
  • Builds: you need a C toolchain, cross-compiling becomes painful, builds get slower, and you lose the easy fully static binary. With cgo on, net (DNS) and os/user use libc and the binary links dynamically. With CGO_ENABLED=0 those packages fall back to pure-Go implementations.
  • Safety: C memory is invisible to the GC and the race detector, a crash in C takes down the process, and the pointer-passing rules are strict.
  • Tooling: pprof cannot see C stacks, and debugging is harder.
package main

/*
#include <stdlib.h>
#include <string.h>
static size_t clen(const char* s) { return strlen(s); }
*/
import "C"

import (
    "fmt"
    "unsafe"
)

func main() {
    cs := C.CString("hello")          // malloc'd copy: must free
    defer C.free(unsafe.Pointer(cs))
    fmt.Println(int(C.clen(cs)))      // 5
}

When to use it: mature C libraries with no Go equivalent (SQLite, libvips, GPU drivers). Alternatives: pure-Go ports (modernc.org/sqlite), a subprocess or RPC sidecar, purego-style dynamic loading, or WASM. Interviewers want you to name the overhead, static-build and cross-compilation costs, and batching.

More on Modules, Packages & Tooling

All 36 Modules, Packages & Tooling questions