Understanding second argument to errors.As method

Why does the second argument to the errors.As method need to be a pointer to a pointer?

func main() {
	if _, err := os.Open("non-existing"); err != nil {
		var pathError *fs.PathError
		if errors.As(err, &pathError) {
			fmt.Println("Failed at path:", pathError.Path)
		} else {
			fmt.Println(err)
		}
	}
}

When trying to just use a pointer to an error type, from gopls, I get second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type. I don’t quite get the error.

Hello there. You are passing pointer to a value, since As function needs to assert type of the error and assign it to a passed value, according to docs:

// As finds the first error in err’s tree that matches target, and if one is found, sets
// target to that error value and returns true. Otherwise, it returns false.

Sorry, but could you explain a bit. I still don’t get it.

The error says that the the second argument of errors.As should be a pointer to the type that implements error. In this case, the type that implements error is *fs.PathError (not fs.PathError). So, “a pointer to the type that implements error” means “a pointer to the *fs.PathError” which means **fs.PathError.

Sometimes, it doesn’t have to be a pointer to pointer. Here is an example: The Go Playground. In this example, the error type is MyCustomError (not *MyCustomError), that’s why we can just pass MyCustomError.

Note that, we can also create a custom method As to convert between error. Here is an example: The Go Playground. In this example, ErrorA implements As, so we can match between ErrorA and ErrorB.


Now, if your question is “why did Go design it like this?”, here is my point of view: errors.As has to accept a pointer as their second argument. It has to. Otherwise, they can’t modify it. Only when we pass a pointer we can modify it. Of course, they can make a special case, maybe if the target is already a pointer, we don’t have to make it a pointer to pointer. But, that will be confusing because there is a special handling for pointer. It would be easier if we just use a general rule for all types.

1 Like

I finally get it. Thank you very much!!