Describe Go slice internals and compare to Rust slice and Vec
Memory layout and ownership of buffers.
Go slices are three-word headers over an array; Rust &[T] is a two-word borrow without capacity; Vec<T> is an owned buffer that reallocates.
WHAT THIS TESTS: This question probes your understanding of memory layout, value semantics, and ownership models in two systems languages. Interviewers want to see that you know a Go slice is not the array itself but a descriptor over a fixed backing array, and that you can contrast this with Rust's explicit split between borrowed slices and owned vectors.
A GOOD ANSWER COVERS: Four points in order. First, the Go slice header: it is a small struct-like value containing a pointer to the zeroth element of an underlying array, an integer length, and an integer capacity. The underlying array is a fixed-size contiguous block; make allocates this array and returns a slice referencing it. Second, slicing in Go creates a new header pointing into the same array, which is why mutations through overlapping slices are visible to each other. Third, Rust &[T] is a fat pointer: exactly two words, a pointer and a length. It carries no capacity because it does not own or manage the buffer; it is a borrowed view whose lifetime is checked at compile time. Fourth, Vec<T> in Rust is an owned heap allocation with a pointer, length, and capacity. When push exceeds capacity, it reallocates to a new heap region, typically doubling capacity, and moves elements.
COMMON WRONG ANSWERS: Saying a Go slice is just a pointer or that it directly contains elements. Another red flag is claiming Rust &[T] stores capacity or can be resized; a shared slice is immutable through the reference and cannot grow. Confusing Go array value semantics, arrays copy on assignment, with slice header copying, which only copies the descriptor while sharing the backing array. Also, omitting that Go append may allocate a new array when capacity is exhausted.
LIKELY FOLLOW-UPS: How does Go append work when capacity is exceeded? What happens to existing slice headers after reallocation? How would you prevent memory leaks with large underlying arrays retained by small slices? In Rust, why does Vec push take mutable self while slice indexing can use shared self? How does Rust prevent use-after-free when a Vec reallocates while slices reference it?
ONE CONCRETE EXAMPLE: Imagine b := []byte{'g','o','l','a','n','g'} in Go. The slice b has length 6 and capacity 6. The expression b[1:4] produces a new slice header with pointer offset to 'o', length 3, and capacity 5. Modifying index 0 of the new slice changes b[1]. In Rust, let v = vec!['g','o','l','a','n','g']; let s = &v[1..4]; creates a two-word &[char] pointing into the Vec's heap buffer. You cannot push into v while s is borrowed because the borrow checker prevents invalidation of the slice.
Read the original → go.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.