Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nil is not nill in some cases #70364

Closed
okm-8 opened this issue Nov 15, 2024 · 3 comments
Closed

Nil is not nill in some cases #70364

okm-8 opened this issue Nov 15, 2024 · 3 comments

Comments

@okm-8
Copy link

okm-8 commented Nov 15, 2024

Go version

go version go1.23.1 darwin/arm64

Output of go env in your module/workspace:

GO111MODULE='on'
GOARCH='arm64'
GOBIN=''
GOCACHE='~/Library/Caches/go-build'
GOENV='~/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='~/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='~/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/opt/go/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/opt/homebrew/opt/go/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.23.1'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='~/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOARM64='v8.0'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='~/projects/sandbox/golang/go.mod'
GOWORK='~/projects/sandbox/golang/go.work'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/xk/mlc_37qd2jj8pcjkmsf9gqb40000gn/T/go-build3449149938=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

I wrote custom error

type TestError struct {
	err error
}

func New(err error) *TestError {
	return &TestError{err: err}
}

func (err *TestError) Error() string {
	if err == nil {
		return ""
	}

	return err.err.Error()
}

func (err *TestError) Unwrap() error {
	return err.err
}

What did you see happen?

When a fuction return nil typed as my custom error, but stored as error{} it is not equal to nill

func doSomething() error {
	return nil
}

func doSomethingElse() *TestError {
	return nil
}

func main() {
	var err error = doSomething()
	var err2 error = doSomethingElse()
	customErr := doSomethingElse()
	customErr2 := doSomethingElse()

	if err != nil {
		fmt.Printf("err: Not nil, type: %T, address: %p\n", err, err)
	} else {
		fmt.Printf("err: Nil, type: %T, address: %p\n", err, err)
	}

	if err2 != nil {
		fmt.Printf("err2: Not nil, type: %T, address: %p\n", err2, err2)
	} else {
		fmt.Printf("err2: Nil, type: %T, address: %p\n", err2, err2)
	}

	if customErr != nil {
		fmt.Printf("customErr: Not nil, type: %T, address: %p\n", customErr, customErr)
	} else {
		fmt.Printf("customErr: Nil, type: %T, address: %p\n", customErr, customErr)
	}

	if customErr2 != nil {
		fmt.Printf("customErr2: Not nil, type: %T, address: %p\n", customErr2, customErr2)
	} else {
		fmt.Printf("customErr2: Nil, type: %T, address: %p\n", customErr2, customErr2)
	}

	// Output
	// err: Nil, type: <nil>, address: %!p(<nil>) - expected
	// err2: Not nil, type: *main.TestError, address: 0x0 - unexpected ???
	// customErr: Nil, type: *main.TestError, address: 0x0 - expected
	// customErr2: Nil, type: *main.TestError, address: 0x0 - expected
}

What did you expect to see?

I expect nil to be equal to nil.

@okm-8 okm-8 changed the title import/path: issue title Nil is not nill in some cases Nov 15, 2024
@okm-8
Copy link
Author

okm-8 commented Nov 15, 2024

Sorry looks like it is "normal" behaviour 🤦
https://go.dev/doc/faq#nil_error

@okm-8
Copy link
Author

okm-8 commented Nov 15, 2024

After learning FAQ I found more general issue. This behaviour makes nill unprocessable in some normal way

package main

import (
	"fmt"
)

type Unwrapable interface {
	Unwrap() error
}

type TestStruct struct {
	err error
}

func (err *TestStruct) Unwrap() error {
	return err.err
}

func doSomethingElse() *TestStruct {
	return nil  // nil is normal value for pointer
}

func HandleUnwrapable(err Unwrapable) {
        // I cannot handle nil here ever
	if err != nil {
		fmt.Printf("err: Not nil, type: %T, address: %p\n", err, err)
	} else {
		fmt.Printf("err: Nil, type: %T, address: %p\n", err, err)
	}
}

func main() {
	test := doSomethingElse()

	if test != nil {
		fmt.Printf("err: Not nil, type: %T, address: %p\n", test, test)
	} else {
		fmt.Printf("err: Nil, type: %T, address: %p\n", test, test)
	}

	HandleUnwrapable(test)

	// Output
	// err: Nil, type: *main.TestStruct, address: 0x0
        // err: Not nil, type: *main.TestStruct, address: 0x0
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants