Consider the following code for structs and pointer reference example.
package main
import (
"fmt"
"sync"
)
type myStruct struct {
mut *sync.RWMutex
}
type arrayOfMyStruct struct {
structList []myStruct
}
func main() {
k := &sync.RWMutex{}
myStructInstance := myStruct{k}
fmt.Printf("%p", &k) // address 1 -> address of k
fmt.Println("")
fmt.Printf("%p", &*k) // address 2 -> address of what k points to
fmt.Println("")
var listStruct []myStruct
listStruct = append(listStruct, myStructInstance)
test := &arrayOfMyStruct{listStruct}
test.access()
}
func (elem *arrayOfMyStruct) access() {
mystructinstance := &elem.structList[0]
mystructinstance2 := elem.structList[0]
fmt.Println(&mystructinstance.mut) // address 3
fmt.Println(&mystructinstance2.mut) // address 4
}
Why are addresses 2 3 and 4 different? Shouldn't they be the same?
It's because mut
in myStruct
is already a pointer *sync.RWMutex
, so when you do:
&mystructinstance2.mut
You are actually getting a new pointer to mut
which is a pointer to the mutex something like **sync.RWMutex
. Just remove the &
on the print statements. When you use k := &sync.RWMutex{}
to create the mutex it's already a pointer, so you shouldn't use &
anywhere after that on that variable or it will create a new pointer to that pointer. You can also use k := new(sync.RWMutex)
to create a pointer to a new mutex.
You can use %p
on Printf to print the pointer address:
fmt.Printf("%p
", mystructinstance2.mut)
Address 1 is the address of the pointer to your mutex. Address 2 is the address of your mutex. That is, 2 points to where the mutex bits live, 1 points to where the pointer in 2 lives. Printing with %T
instead of %p
will show you the types of the values are different.
The append
actually copies your struct. The struct instance is a value just like an int
is; when you append a struct to the list, you are actually copying the values of each field inside it.
Value semantics for structs are widely used in C and Go (and also present in e.g. C# ValueTypes), but less common in, for example, Java, JavaScript, or Python. They mean you sometimes have to think about whether things are pointers or not, but they can save you some mistakes from accidentally making a change in one place that has an effect somewhere else (aliasing), and reduce the number of pointers the garbage collector has to follow.
Address 3 is the address of the mutex pointer in the copy of your struct created by append
.
The assignment to mystructinstance2
also copies the value, so now there are three copies of it floating around. Address 4 is the address of the mutex pointer in this new instance.
You want
k := elem.structList[0].mut
p1 := &*k
or, simply,
p2 := &*elem.structList[0].mut
For example,
package main
import (
"fmt"
"sync"
)
type myStruct struct {
mut *sync.RWMutex
}
type arrayOfMyStruct struct {
structList []myStruct
}
func main() {
k := &sync.RWMutex{}
myStructInstance := myStruct{k}
fmt.Printf("%p
", &*k)
var listStruct []myStruct
listStruct = append(listStruct, myStructInstance)
test := &arrayOfMyStruct{listStruct}
test.access()
}
func (elem *arrayOfMyStruct) access() {
k := elem.structList[0].mut
p1 := &*k
fmt.Printf("%p
", p1)
p2 := &*elem.structList[0].mut
fmt.Printf("%p
", p2)
}
Output:
0xc4200142c0
0xc4200142c0
0xc4200142c0