What does slices.Clone(s) copy that the plain assignment b := s does not?
answer
- two names, one array
- the header is three words
- assignment copies the header, not the elements
- Clone allocates a new array
- shallow: pointers and inner slices stay shared
basics
~10 sslices.Clone allocates a new backing array and copies the elements into it, so the two slices no longer share storage. Plain assignment copies only the slice header, leaving both names pointing at one array.
solid answer
~40 sA Go slice value is a three-word header: a pointer to a backing array, a length and a capacity. `b := s` copies that header, so `b` and `s` are two independent headers over one shared array, and `b[0] = 9` is visible through `s`. `slices.Clone(s)` allocates a fresh array of `len(s)` elements, copies the elements into it with ordinary assignment, and returns a header pointing at the new array, so later writes to either slice are invisible to the other. The copy is shallow: if the element type is a pointer, a slice, a map, or a struct containing one of those, the clone shares whatever those elements refer to. And `slices.Clone` of a nil slice returns nil rather than an empty non-nil slice.
code
go · 6 linesa := []int{1, 2, 3}
b := a // copies the header only
b[0] = 9 // a[0] is now 9 as well
c := slices.Clone(a) // new array, elements copied
c[1] = 8 // a[1] is unchangedgo deeper
Be ready to say that assigning a slice copies only its header, that both names then share one array, and that slices.Clone is what gives you an independent copy.
Explain the three-word header, why Clone allocates a new array of len(s), and why the copy is shallow for pointer, slice and map element types.
Show where a shallow clone bites in review: a snapshot handed to another goroutine or kept for later comparison whose nested slices still alias live data.
Own the API rule for a package other teams import: either return a defensive clone or document the returned slice as read-only, and apply the same choice across every function.
## The thing a slice variable holds A Go slice value is a small, fixed-size header made of three words: a **pointer** to the first element of a backing array, a **length** (how many elements this view exposes) and a **capacity** (how many array slots are available from that pointer onward). The elements themselves are *not* in the slice value. They live in an array somewhere else in memory, and the slice is a view onto it. Every assignment in Go copies the value on the right-hand side. For a slice that means the header is copied — pointer, length, capacity — and nothing else. So after ```go a := []int{1, 2, 3} b := a ``` there are two independent headers and exactly **one** array. `b` is not an alias of the variable `a`: reassigning `b = append(b, 4)` or `b = b[:1]` changes only `b`'s own header. But `b[0] = 9` reaches through the shared pointer and writes into the one array, so `a[0]` is now `9` as well. This is the single most common surprise for engineers arriving from a language where collections are objects with reference semantics, and it is also what makes passing a slice to a function cheap: you copy 24 bytes on a 64-bit machine, never the data. ## What slices.Clone does ```go b := slices.Clone(a) ``` `slices.Clone` allocates a **new backing array** with room for `len(a)` elements, copies the elements into it, and returns a header pointing at the new array. From that moment `a` and `b` have no storage in common, so `b[0] = 9` is invisible to `a` and `a[0] = 7` is invisible to `b`. The returned slice has the same length as the input; its capacity is at least that length and the documentation deliberately allows extra unused capacity, so do not write code that depends on `cap(slices.Clone(a)) == len(a)`. One special case worth memorising: **cloning a nil slice gives you a nil slice**, not an empty non-nil one. There is no array to allocate, so none is allocated. That matters downstream, because `reflect.DeepEqual` treats a nil slice and an empty slice as different and `encoding/json` writes `null` for the first and `[]` for the second. ## Shallow means shallow `slices.Clone` copies each element **using ordinary assignment** — exactly what `=` would do for that element type. For `[]int`, `[]string` or `[]time.Duration` that is a complete copy and the clone is fully independent. For anything whose value contains a pointer, it is not: - `[]*User` — the pointers are duplicated, the `User` structs behind them are shared. - `[][]byte` — each inner header is duplicated, each inner array is shared. - `[]map[string]string` — each map value is a pointer to one run-time hash table; both slices reach the same table, so a `delete` through the clone is visible through the original. - `[]struct{ Name string; Tags []string }` — the `Name` string data is immutable so it is effectively copied, but every `Tags` header is copied while its array stays shared. So `slices.Clone` gives you independence **at exactly one level**. If your element type owns a nested container and you need a genuinely independent copy, you must clone the nested containers yourself, element by element. The same rule applies to `maps.Clone`, which copies the key/value pairs by assignment: a `map[string][]string` clone shares every value slice. ## The related tools, and how they differ - `copy(dst, src)` is the builtin. You allocate `dst` yourself and it moves `min(len(dst), len(src))` elements. A `dst` you forgot to `make` with a length stays empty and `copy` silently reports 0 — a classic bug that `slices.Clone` removes by doing the allocation for you. - `append(s[:0:0], s...)` is the pre-generics idiom that `slices.Clone` replaced; the `:0:0` three-index slice forces `append` to allocate rather than write into the original's spare capacity. - `bytes.Clone` is the `[]byte`-specific equivalent, useful when you are handed a buffer whose owner will reuse it. None of these are deep copies. Go has no built-in deep copy, and that is a deliberate omission — a correct deep copy requires knowing what your types mean. ## Where this shows up in real code The pattern to internalise is: **a slice you return, store or hand to another goroutine is a promise about storage, not just about values.** If a method returns `s.items` directly, the caller can write through it and mutate your struct's state. If you hold a slice that came from a decoder or a pooled buffer, the owner may reuse that array under you. `slices.Clone` at the boundary is the cheap fix for both — provided the element type has no nested containers, in which case the clone is a comforting illusion rather than a defence. The reviewing question is always the same: after this copy, which memory is still shared, and who is allowed to write to it?
- Is slices.Clone a deep copy of a slice of structs that each hold a Tags []string field?No. Clone copies each element with ordinary assignment, so every Tags header is duplicated but the string arrays behind them are shared. Writing tags[0] = "dev" through the clone is visible in the original. For real independence you must clone each nested slice yourself, element by element.
- What does slices.Clone return when it is given a nil slice?nil. There is no array to allocate, so none is allocated and the result is a nil slice. That matters downstream: reflect.DeepEqual treats a nil slice and an empty one as different, and encoding/json writes null for the first and [] for the second.
- How does the copy builtin differ from slices.Clone?copy(dst, src) requires you to allocate dst yourself and moves min(len(dst), len(src)) elements, so a dst you forgot to size takes nothing and copy silently reports 0. slices.Clone does the allocation at exactly len(src) for you. Both assign elements one by one, so both are equally shallow.
saying these in an interview costs you the question
- Says b := s copies the elements
- Calls slices.Clone a deep copy
- Thinks cloning a slice of pointers duplicates the pointed-to values
- Believes a write through a copy can never reach the original
- Expects slices.Clone(nil) to return an empty non-nil slice