Why doesn't dereferencing a nil pointer in unsafe.Sizeof() cause a panic?

Issue

https://go.dev/play/p/X_BH4qGgXHJ

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    var i *int
    fmt.Println(unsafe.Sizeof(*i)) // dereference of null pointer i
}

Why doesn’t this code

unsafe.Sizeof(*i)

cause a runtime panic?

Solution

Spec: Package unsafe:

Calls to Alignof, Offsetof, and Sizeof are compile-time constant expressions of type uintptr.

These functions are evaluated at compile time, no actual dereferencing happens at runtime.

This is possible because the pointed value is not needed, only information about its type is needed, which does not need dereferencing.

It’s also documented at unsafe.Sizeof():

The return value of Sizeof is a Go constant.

Constants in Go are compile-time constants.

Also see Spec: Constants:

A constant value is represented by a rune, integer, floating-point, imaginary, or string literal, an identifier denoting a constant, a constant expression, a conversion with a result that is a constant, or the result value of some built-in functions such as unsafe.Sizeof applied to any value, cap or len applied to some expressions, real and imag applied to a complex constant and complex applied to numeric constants.

See similar examples (which without passing them to unsafe.Sizeof() would panic at runtime or block indefinitely, but they work just fine):

fmt.Println(unsafe.Sizeof(*(*int8)(nil))) // 1, no dereferencing
fmt.Println(unsafe.Sizeof([]int16{}[10])) // 2, no indexing
x := "hi"
fmt.Println(unsafe.Sizeof(x[10]))                          // 1, no indexing
fmt.Println(unsafe.Sizeof(map[interface{}]int{}[[]int{}])) // 8, no indexing
fmt.Println(unsafe.Sizeof((interface{})(nil).(int16)))     // 2, no type assertion
i := 0
fmt.Println(unsafe.Sizeof(i / i)) // 8, no division by 0
i = -1
fmt.Println(unsafe.Sizeof(1 << i))                // 8, no negative shift count
fmt.Println(unsafe.Sizeof(make([]int, i)))        // 24, no negative length
fmt.Println(unsafe.Sizeof((*[1]int)([]int{})))    // 8, no converting to bigger array
fmt.Println(unsafe.Sizeof((func() int32)(nil)())) // 4, no function call
fmt.Println(unsafe.Sizeof(<-(chan int64)(nil)))   // 8, no receiving
var a, b interface{} = []int{}, []int{}
fmt.Println(unsafe.Sizeof(a == b)) // 1, no comparison

Try these on the Go Playground.

Answered By – icza

Answer Checked By – Katrina (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.