Mutating a slice field of a struct even though all methods are defined with value receivers

Issue

6.2 Methods with a Pointer Receiver

If all the methods of a named type T have a receiver type of T itself
(not *T ), it is safe to copy instances of that type; calling any of
its methods necessarily makes a copy. For example, time.Duration
values are liberally copied, including as arguments to functions. But
if any method has a pointer receiver, you should avoid copying
instances of T because doing so may violate internal invariants. For
example, copying an instance of bytes.Buffer would cause the original
and the copy to alias ( §2.3.2 ) the same underlying array of bytes.
Subsequent method calls would have unpredictable effects.

(The Go Programming Language Alan A. A. Donovan · Brian W. Kernighan)

I understand the general meaning of the quote, but I am wondering whether it’s correct to say that is safe to copy instances of that type.

If a struct has a slice/map field then all copies receive their own copies of the pointers to the backing array/hashmap so it is still possible to mutate those data structures.

Even though all the methods might be defined using value receivers, we can break the internal state of the struct.

I understand why that happens, but doesn’t that possibility contradict what is written in that paragraph above?

Copying values might have unwanted consequences regardless of the method receivers and also depends on the field types.

What am I missing here?

package main

import "fmt"

type T struct {
    s []string
}

func main() {
    original := T{s: []string{"original"}}
    copycat := original
    copycat.s[0] = "copycat"
    fmt.Println(original.s[0] == "copycat") // true
}

Solution

I’m neither Donovan nor Kernighan, so I can’t definitively say what they were trying to communicate here, but my understanding is not that "using value receivers makes copying safe", but rather "using value receivers indicates copying is safe". You are correct that any pointer field, or any field which contains a pointer field (including slices and maps), will make copying unsafe; I believe what the authors are trying to get across is that an API which uses a value receiver is indicating to its consumers that no such fields exist.

Answered By – Adrian

Answer Checked By – David Marino (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.