Immutable Go Object

Every Go programmer knows about the receiver in go, which be declared as:

type X struct {}

func (receiver X) doThing() {...}

We have two types of receiver in Golang, which is Value receiver and Pointer receiver. Basically, the receiver in Golang could be map to self in other programming languages and the function which uses the receiver will be pointed from struct type of the receiver.

So, what does this means, anyway?

In simple terms, Value receiver makes a copy of the type and passes it to the function. The function stack now holds an equal object but at a different location on memory.

Pointer receiver passes the address of a type to the function. The function stack has a reference to the original object.

We can use a simple snipet to check that difference.

type MyType struct {
    val int
}

func (m MyType) valueReceiverDoThing(val int) {
    m.val = val
}

func (m *MyType) pointerReceiverDoThing(val int) {
    m.val = val
}

func main() {
    t := MyType{val: 0}

    t.valueReceiverDoThing(1)
    println(t.val) // expect 0

    t.pointerReceiverDoThing(1)
    println(t.val) // expect 1
}

Since both receiver types can be used to enable the function to gain the access to data encapsulated in the object of that type, the main difference is that Value receiver function can not make changes in the data of the original object because it just has the clone of the object. So basically, we can create an immutable object from a struct if all the struct’s fields are private and all the methods are value receiver function (we have an exception, that is the structure has pointer type field).

So, what does this uses for?

From software architecture view point, this characteristic matches with entity & value object terms.

An entity is an object defined primarily by its identity. We care about its life cycle since we track for its among other objects by its identity, a mutable object is suited for that requirement.

On the other side, a value object reprensents a descriptive aspect of the domain with no conceptual identity. It should be immutate and recreate when it’s been needed, otherwise should be clear by GC. So an object from a structure which all methods it has is value receiver function is suited in this case.

Besides, when we think about immutable, it’s normal if we think about how does it been applied in concurrent patterns. In fact, if your value can be accessed from different routines, an immutable object is a better choice.

Passing a whole object through a channel as a message may costly, but we paid for safety. In case the message you want to send is a big object and you really care about the performance, then a reference to an immutable object be sent as a message sound good to me :)

Hope this note helps you make a wise choice.

FYI, set of methods which registered into a struct type T call method set of T. The method set of a type T consists of all methods with receiver type T, while that of the corresponding pointer type *T consists of all methods with receiver *T or T. More about this go here.