skip to content

Low-Level and Foreign Code

Every escape hatch trades a guarantee away: reflect gives up compile-time typing, unsafe the collector's protection, cgo the self-contained binary, and a non-native target gives up threads.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

explore

questions

79 · 5 sections

What do reflect.TypeOf and reflect.ValueOf return, and how do you get an ordinary Go value back?

level: juniorimportance: must knowfreq 48%
basics
~20 s

reflect.TypeOf returns a reflect.Type describing the dynamic type of whatever you pass it; reflect.ValueOf returns a reflect.Value wrapping the data itself. Value.Interface() hands the data back as an any, which you then type-assert to a concrete type.

open as a page

What does reflect.New(t) return in Go, and why do you call Elem() on the result?

level: juniorimportance: must knowfreq 34%
basics
~20 s

reflect.New(t) allocates a zero value of type t and returns a reflect.Value holding a pointer to it, exactly like new(T). Elem() follows that pointer to the value itself, which lives in addressable memory and can be filled in.

open as a page

Why does reflect.DeepEqual report a nil []string and an empty []string{} as unequal?

level: juniorimportance: must knowfreq 55%
basics
~20 s

reflect.DeepEqual requires two slices to be both nil or both non-nil before it compares length and elements. A nil slice and an empty slice fail that first check, so it reports false even though both have length zero.

open as a page

Why does writing a field via reflect.ValueOf(cfg) panic when reflect.ValueOf(&cfg).Elem() works?

level: juniorimportance: must knowfreq 55%
basics
~20 s

reflect.ValueOf copies its argument into an interface, so the struct it holds has no address and every Set call panics. Passing &cfg and then calling Elem aims reflect at the original variable, which is addressable and therefore settable.

open as a page

Using Go's reflect package, how do you list a struct's fields and read each field's struct tag?

level: juniorimportance: must knowfreq 50%
basics
~10 s

reflect.TypeOf gives you the struct's Type; Type.NumField reports how many fields it declares and Type.Field(i) returns a StructField holding that field's Name, Type and Tag. Tag.Get reads one tag key out of it.

open as a page

How wide are Go's int, uint and uintptr when you build for GOARCH=386 or arm?

level: juniorimportance: must knowfreq 48%
basics
~20 s

Go's int and uint are 64 bits on amd64 and arm64 but only 32 bits on 386, arm and the other 32-bit targets, and uintptr is always pointer-sized. The sized types, int8 through int64 and float64, never change.

open as a page

What does building with GOOS=js GOARCH=wasm produce, and why does the page also need wasm_exec.js?

level: juniorimportance: must knowfreq 35%
basics
~20 s

It produces a .wasm module, not a standalone program. The module declares imports for the host functions the Go runtime needs, and wasm_exec.js is the JavaScript glue that ships with the toolchain, supplies those imports and starts the program.

open as a page

Why does atomic.AddUint64 on a struct field panic on GOARCH=386 but not on amd64?

level: middleimportance: should knowfreq 32%
basics
~20 s

On 32-bit targets a 64-bit struct field only needs 4-byte alignment, while the 64-bit atomic functions require an 8-byte-aligned address, so they panic there. On 64-bit targets the field lands aligned anyway, hiding the bug.

open as a page

When is binary.NativeEndian the right choice in Go, and when is it a portability bug?

level: middleimportance: should knowfreq 30%
basics
~20 s

binary.NativeEndian encodes in whatever byte order the build target uses, so it fits only bytes that never leave the machine. Anything written to a wire or a shared file must name binary.BigEndian or binary.LittleEndian instead.

open as a page

How do you expose a Go function to JavaScript with syscall/js, and why must main not return?

level: middleimportance: should knowfreq 30%
basics
~20 s

Wrap the Go function with js.FuncOf and attach it to a JavaScript object, usually via js.Global().Set. The program exits when main returns, and once it has exited the wrapper can no longer be called, so a callback-serving module blocks forever.

open as a page

Why must a Go uintptr-to-unsafe.Pointer round trip with arithmetic happen in one expression?

level: middleimportance: must knowfreq 55%
basics
~20 s

Go's garbage collector does not treat a uintptr as a reference. If the address rests in a variable between statements, the object can be collected, or moved when a goroutine stack is copied, so the address converted back is stale.

open as a page

What does unsafe.Sizeof report for a Go string, slice or map value, and what does it not count?

level: middleimportance: must knowfreq 45%
basics
~20 s

unsafe.Sizeof counts only a value's own fixed representation: on a 64-bit platform 16 bytes for any string, 24 for any slice and 8 for any map. The bytes, elements and hash table those pointers reach are never counted.

open as a page

Why is writing through unsafe.StringData(s) to a string's bytes undefined rather than a hack that happens to work?

level: middleimportance: must knowfreq 40%
basics
~20 s

Go guarantees a string never changes, and the compiler, runtime and library all build on that. Literals may sit in read-only memory, copies share one array, and a mutated map key stays stranded under its old hash.

open as a page

What is Go's unsafe.Pointer, and which pointer conversions does it make legal?

level: juniorimportance: should knowfreq 35%
basics
~20 s

unsafe.Pointer is a pointer type with no element type. Any typed pointer converts to it and back out as a different typed pointer, and it converts to and from uintptr. It is Go's only bridge between unrelated pointer types.

open as a page

What does Go's unsafe.Sizeof(x) return, and is it computed at run time or at compile time?

level: juniorimportance: should knowfreq 30%
basics
~20 s

unsafe.Sizeof(x) gives the size in bytes of x's own type representation. The compiler replaces the call with a uintptr constant, so no code runs for it and the result never depends on the contents of x.

open as a page

In cgo, what do C.CBytes and C.GoBytes do, and who frees the C copy?

level: juniorimportance: must knowfreq 45%
basics
~10 s

C.CBytes copies a Go []byte into malloc'd C memory and returns an unsafe.Pointer you must release with C.free. C.GoBytes copies C memory back into a brand-new Go slice that the garbage collector owns.

open as a page

When a Go program is built without cgo, what handles hostname and user lookups instead of libc?

level: middleimportance: must knowfreq 55%
basics
~10 s

Go's own implementations take over. The pure-Go resolver parses /etc/resolv.conf and /etc/hosts and speaks DNS itself, and os/user parses /etc/passwd and /etc/group. Neither consults nsswitch.conf or the host's pluggable name-service modules.

open as a page

In cgo, what does C.CString return, and who is responsible for freeing it?

level: middleimportance: must knowfreq 38%
basics
~10 s

C.CString copies a Go string into memory allocated by C's malloc and returns a *C.char. Go's garbage collector never manages that memory, so the caller must release it with C.free(unsafe.Pointer(p)).

open as a page

Why is calling a C function from Go through cgo far more expensive than a plain Go function call?

level: middleimportance: must knowfreq 42%
basics
~20 s

A cgo call is a runtime transition, not a jump. The goroutine moves onto the thread's system stack, the runtime records that it has left Go code, and both are undone on return. That costs tens of nanoseconds; a Go call costs a couple.

open as a page

What are cgo's rules for passing a Go pointer into C code?

level: middleimportance: must knowfreq 55%
basics
~20 s

Pass a Go pointer to C only if the memory it points at holds no Go pointers, and C must not keep it after the call returns. C may never store a Go pointer into Go memory.

open as a page

What is syscall.Errno, and how do you test whether an error is a specific errno such as ENOENT?

level: juniorimportance: must knowfreq 38%
basics
~20 s

syscall.Errno is an integer type (a uintptr) whose Error method makes the number itself satisfy Go's error interface. Test for a particular errno with errors.Is(err, syscall.ENOENT), which unwraps wrappers such as *os.PathError instead of matching message text.

open as a page

Why use os.File.SyscallConn and RawConn.Control instead of os.File.Fd?

level: middleimportance: must knowfreq 35%
basics
~20 s

os.File.SyscallConn returns a syscall.RawConn whose Control method runs your callback with the descriptor guaranteed open for exactly that call. Fd hands out a bare number with no such guarantee and takes the file out of the runtime's poller.

open as a page

What does the Fd method on Go's *os.File return, and how long is that number valid?

level: juniorimportance: should knowfreq 30%
basics
~20 s

os.File.Fd returns the underlying operating-system descriptor number as a uintptr. The *os.File keeps ownership of it, so the number is valid only until that file is closed or garbage collected, and using it afterwards is unsafe.

open as a page

How do you set SO_REUSEADDR on a Go TCP listener's socket before it binds?

level: middleimportance: should knowfreq 26%
basics
~10 s

Use net.ListenConfig with a Control function. Go calls it after creating the socket but before binding, hands you a syscall.RawConn, and inside its Control callback you call syscall.SetsockoptInt on the descriptor. Then call lc.Listen.

open as a page

Why is the standard library's syscall package frozen, and what does golang.org/x/sys provide instead?

level: middleimportance: should knowfreq 42%
basics
~20 s

Go's syscall package is locked down - no new calls or constants - because the Go 1 compatibility promise would freeze that per-OS surface forever. golang.org/x/sys/unix and x/sys/windows are the maintained replacements, shipped as an ordinary module.

open as a page