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 pageshowhide
explore
- Dynamic Types at Runtime29 questions
- Building Values Dynamically4 questions
- reflect.Value and Kind4 questions
- Deep Equality Semantics4 questions
- Walking Structs and Tags4 questions
- Settability and Addressability4 questions
- Calling Methods Dynamically5 questions
- Cost and Safer Alternatives4 questions
- Non-Native Build Targets11 questions
- WebAssembly and WASI Builds6 questions
- Word Size and Endianness5 questions
- Raw Memory and Layout13 questions
- Pointer Conversion Rules5 questions
- Sizeof, Alignof and Offsetof4 questions
- Zero-Copy String Conversions4 questions
- Calling Into C17 questions
- The cgo Build Model4 questions
- Pointer Passing Rules4 questions
- Overhead and Thread Pinning5 questions
- Static Builds Without cgo4 questions
- Kernel-Level Access9 questions
- Raw syscall and x/sys5 questions
- File Descriptors and RawConn4 questions
questions
79 · 5 sectionsWhat do reflect.TypeOf and reflect.ValueOf return, and how do you get an ordinary Go value back?
basics
~20 sreflect.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.
What does reflect.New(t) return in Go, and why do you call Elem() on the result?
basics
~20 sreflect.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.
Why does reflect.DeepEqual report a nil []string and an empty []string{} as unequal?
basics
~20 sreflect.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.
Why does writing a field via reflect.ValueOf(cfg) panic when reflect.ValueOf(&cfg).Elem() works?
basics
~20 sreflect.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.
Using Go's reflect package, how do you list a struct's fields and read each field's struct tag?
basics
~10 sreflect.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.
How wide are Go's int, uint and uintptr when you build for GOARCH=386 or arm?
basics
~20 sGo'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.
What does building with GOOS=js GOARCH=wasm produce, and why does the page also need wasm_exec.js?
basics
~20 sIt 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.
Why does atomic.AddUint64 on a struct field panic on GOARCH=386 but not on amd64?
basics
~20 sOn 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.
When is binary.NativeEndian the right choice in Go, and when is it a portability bug?
basics
~20 sbinary.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.
How do you expose a Go function to JavaScript with syscall/js, and why must main not return?
basics
~20 sWrap 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.
Why must a Go uintptr-to-unsafe.Pointer round trip with arithmetic happen in one expression?
basics
~20 sGo'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.
What does unsafe.Sizeof report for a Go string, slice or map value, and what does it not count?
basics
~20 sunsafe.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.
Why is writing through unsafe.StringData(s) to a string's bytes undefined rather than a hack that happens to work?
basics
~20 sGo 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.
What is Go's unsafe.Pointer, and which pointer conversions does it make legal?
basics
~20 sunsafe.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.
What does Go's unsafe.Sizeof(x) return, and is it computed at run time or at compile time?
basics
~20 sunsafe.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.
In cgo, what do C.CBytes and C.GoBytes do, and who frees the C copy?
basics
~10 sC.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.
When a Go program is built without cgo, what handles hostname and user lookups instead of libc?
basics
~10 sGo'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.
In cgo, what does C.CString return, and who is responsible for freeing it?
basics
~10 sC.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)).
Why is calling a C function from Go through cgo far more expensive than a plain Go function call?
basics
~20 sA 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.
What are cgo's rules for passing a Go pointer into C code?
basics
~20 sPass 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.
What is syscall.Errno, and how do you test whether an error is a specific errno such as ENOENT?
basics
~20 ssyscall.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.
Why use os.File.SyscallConn and RawConn.Control instead of os.File.Fd?
basics
~20 sos.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.
What does the Fd method on Go's *os.File return, and how long is that number valid?
basics
~20 sos.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.
How do you set SO_REUSEADDR on a Go TCP listener's socket before it binds?
basics
~10 sUse 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.
Why is the standard library's syscall package frozen, and what does golang.org/x/sys provide instead?
basics
~20 sGo'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.