GO ADVANCED
unsafe.Pointer
, uintptr
seem like total black magic to many Go developers. We often just use them as special spells for packages without really understanding how they work under the hood.
And admittedly, they are seriously magical, let me show you an example of how I can use unsafe pointers to rejuvenate a person.
type Person struct {
Name string
age int
}
func main() {
person := Person{Name: "John", age: 30}
// Cast name to unsafe pointer
namePtr := unsafe.Pointer(&person.Name)
nameSize := unsafe.Sizeof(person.Name)
p := (*int)(unsafe.Add(namePtr, nameSize))
*p = 10
fmt.Println(person) // {John 10}
}
By doing some unsafe pointer math, I can modify the private age
field through the public Name
field. This approach is totally unintuitive, but it works, changing John’s age from 30 to 10.
Understanding how to safely leverage unsafe pointers enables building powerful functionality.
First look at unsafe package
Beside unsafe.Pointer
, this package provide us several functions provide low-level information, make us understand more about the internal structure of type.
unsafe.Alignof
The alignment is a concept which bring from the low-level programming, it returns the required alignment of a type and the layout of memory can affect the performance of our application.
For example, an int32
has an alignment of 4 bytes. The alignment of a struct depends on the field with the largest alignment:
type Person struct {
Name string
age int
}
func main() {
var i int32 = 1
var s [3]int8 = [3]int8{1, 2, 3}
var p Person = Person{"Bob", 20}
fmt.Println("aligno(int32) =", unsafe.Alignof(i))
fmt.Println("alignof([]int8) =", unsafe.Alignof(s))
fmt.Println("alignof(Person) =", unsafe.Alignof(p))
}
// aligno(int32) = 4
// alignof([3]int8) = 1
// alignof(Person) = 8