I know that pointers in Go allow mutation of a function's arguments, but wouldn't it have been simpler if they adopted just references (with appropriate const or mutable qualifiers). Now we have pointers and for some built-in types like maps and channels implicit pass by reference.
Am I missing something or are pointers in Go just an unnecessary complication?
Go has pointers. A pointer holds the memory address of a value. The data is stored in the memory when the program runs and each has a number- the memory address, which can be assigned to a pointer, and through which we can find the data stored in memory.
Accidental Mutation When you pass a pointer to a function, you don't know if it gets edited or not. This adds complexity to your codebase and as the code grows, it becomes really easy for an error to slip in because somewhere deep in the call stack, the pointer struct is changed.
In Go a pointer is represented using the * (asterisk) character followed by the type of the stored value. In the zero function xPtr is a pointer to an int . * is also used to “dereference” pointer variables. Dereferencing a pointer gives us access to the value the pointer points to.
But passing pointers in Go is often slower than passing copied values. This is because when pointers are passed into functions, Go needs to perform an escape analysis to work out whether the value needs to be stored on the stack or in the heap.
Pointers are usefull for several reasons. Pointers allow control over memory layout (affects efficiency of CPU cache). In Go we can define a structure where all the members are in contiguous memory:
type Point struct { x, y int } type LineSegment struct { source, destination Point }
In this case the Point
structures are embedded within the LineSegment
struct. But you can't always embed data directly. If you want to support structures such as binary trees or linked list, then you need to support some kind of pointer.
type TreeNode { value int left *TreeNode right *TreeNode }
Java, Python etc doesn't have this problem because it does not allow you to embed composite types, so there is no need to syntactically differentiate between embedding and pointing.
A possible alternative to accomplish the same is to differentiate between struct
and class
as C# and Swift does. But this does have limitations. While you can usually specify that a function takes a struct as an inout
parameter to avoid copying the struct, it doesn't allow you to store references (pointers) to structs. This means you can never treat a struct as a reference type when you find that useful e.g. to create a pool allocator (see below).
Using pointers you can also create your own pool allocator (this is very simplified with lots of checks removed to just show the principle):
type TreeNode { value int left *TreeNode right *TreeNode nextFreeNode *TreeNode; // For memory allocation } var pool [1024]TreeNode var firstFreeNode *TreeNode = &pool[0] func poolAlloc() *TreeNode { node := firstFreeNode firstFreeNode = firstFreeNode.nextFreeNode return node } func freeNode(node *TreeNode) { node.nextFreeNode = firstFreeNode firstFreeNode = node }
Pointers also allows you to implement swap
. That is swapping the values of two variables:
func swap(a *int, b *int) { temp := *a *a = *b *b = temp }
Java has never been able to fully replace C++ for systems programming at places such as Google, in part because performance can not be tuned to the same extend due to the lack of ability to control memory layout and usage (cache misses affect performance significantly). Go has aimed to replace C++ in many areas and thus needs to support pointers.
I really like example taken from http://www.golang-book.com/8
func zero(x int) { x = 0 } func main() { x := 5 zero(x) fmt.Println(x) // x is still 5 }
as contrasted with
func zero(xPtr *int) { *xPtr = 0 } func main() { x := 5 zero(&x) fmt.Println(x) // x is 0 }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With