Type assertion: panic: value method time.Time.String called using nil *Time pointer"

954 views
Skip to first unread message

Markus Heukelom

unread,
Jun 10, 2021, 8:50:23 AM6/10/21
to golang-nuts
Code:

package main

import (
"fmt"
"time"
)

func main() {

var my *time.Time
var any interface{}
any = my

stringer, ok := any.(fmt.Stringer)
println(ok) // "true", but I expected "false", as the receiver for time.Time.String() is (t time.Time), not (t *time.Time)
println(stringer.String()) // "panic: value method time.Time.String called using nil *Time pointer"
}


Question 1:

I don't understand when the type assertion succeeds here, as the receiver for time.Time.String() is (t time.Time), not (t *time.Time). The language specification states:

"If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T."


Does this mean that I should read "dynamic type of x" as "the actual type of x" or the "dereferenced type of x if x is a pointer"? Is there a formal definition of "dynamic type of x"? Or should I look at a different part of the spec?

Question 2:

Accepting that I get "true" on a pointer to a type that implements fmt.Stringer, how can I check that I can actually safely call stringer.String()? I.e. how can I prevent the runtime panic? Without using reflection. Please note: I do not mean checking if there is a nil pointer in x: the error is not raised by the time.String() implementation, in fact it might work fine with nil pointers were it defined using a pointer receiver. It seems the error is thrown by the runtime.

Thanks,

Markus

Axel Wagner

unread,
Jun 10, 2021, 9:05:57 AM6/10/21
to Markus Heukelom, golang-nuts
On Thu, Jun 10, 2021 at 2:50 PM Markus Heukelom <markus....@gain.pro> wrote:
Question 1:

I don't understand when the type assertion succeeds here, as the receiver for time.Time.String() is (t time.Time), not (t *time.Time).

The method set of `*Time` includes all methods declared on `Time` (but not vice-versa):
So even though the receiver of `String() string` is not a pointer, `*Time` still has a `String() string` method.

Does this mean that I should read "dynamic type of x" as "the actual type of x" or the "dereferenced type of x if x is a pointer"? Is there a formal definition of "dynamic type of x"? Or should I look at a different part of the spec?

The dynamic type is explained here: https://golang.org/ref/spec#Variables

The static type (or just type) of a variable is the type given in its declaration, the type provided in the new call or composite literal, or the type of an element of a structured variable. Variables of interface type also have a distinct dynamic type, which is the concrete type of the value assigned to the variable at run time (unless the value is the predeclared identifier nil, which has no type). The dynamic type may vary during execution but values stored in interface variables are always assignable to the static type of the variable.
 
(emphasis mine)

Question 2:

Accepting that I get "true" on a pointer to a type that implements fmt.Stringer, how can I check that I can actually safely call stringer.String()?

You can't. Any Go code might panic. That is, no matter what check you do, the `String()` method could just call `panic("oh well")`.
 
I.e. how can I prevent the runtime panic?

The, probably unsatisfactory, but only really true answer is "by not assigning a nil `*time.Time` to a `fmt.Stringer` and then call `String()` on that". The runtime panic tells you that your code has a bug. The solution to that is to fix the bug.
Note that `time.Time` documents that you shouldn't use it as a pointer in any case.

If it's any consolation: Even if there was a check that's correct, remembering to do that check would be just as hard (if not harder) as remembering not to call the method on a nil-pointer or storing a nil-pointer in a `fmt.Stringer` to begin with :)

Without using reflection. Please note: I do not mean checking if there is a nil pointer in x: the error is not raised by the time.String() implementation, in fact it might work fine with nil pointers were it defined using a pointer receiver. It seems the error is thrown by the runtime.

Thanks,

Markus

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAMoB8rV%3DQTcHXRC4KvJfcHC9zo8oAE0LR9%3D4ywVb_s-iejX14A%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages