1

I have a struct that consists of a custom time.Time defined for the sake of it having a custom MarshalJSON() interface, following this answer's suggestion:

type MyTime time.Time
func (s myTime) MarshalJSON() ([]byte, error) {
    t := time.Time(s)
    return []byte(t.Format(`"20060102T150405Z"`)), nil
}

I define a MyStruct type with ThisDate and ThatDate fields of type *MyTime:

type MyStruct struct {
    ThisDate *MyTime `json:"thisdate,omitempty"`
    ThatDate *MyTime `json:"thatdate,omitempty"`
}

As far as I understand, I need to use *MyTime and not MyTime so the omitempty tag will have an effect when I'll MarshalJSON a variable of this type, following this answer's suggestion.

I use a library that has a function that returns me a struct with some fields of type *time.Time:

someVar := Lib.GetVar()

I tried to define a variable of type MyStruct like this:

myVar := &MyStruct{
    ThisDate: someVar.ThisDate
    ThatDate: someVar.ThatDate
}

Naturally, it gives me a compilation error:

cannot use someVar.ThisDate (variable of type *time.Time) as *MyTime value in struct literal ...

I tried typecasting someVar.ThisDate with */& and without these without luck. I thought the following would work:

myVar := &MyStruct{
    ThisDate: *MyTime(*someVar.ThisDate)
    ThatDate: *MyTime(*someVar.ThatDate)
}

But it gives me a different compilation error:

invalid operation: cannot indirect MyTime(*someVar.ThisDate) (value of type MyTime) ...

It seems I probably lack basic understanding of pointers and dereferences in Go. Never the less, I would like to avoid finding a specific solution for my issue which comes down to the combination of the need to make omitempty have an effect and a custom MarshalJSON.

Doron Behar
  • 2,606
  • 2
  • 21
  • 24
  • `(*MyTime)(someVar.ThisDate)` The `someVar.ThisDate` is already a `*time.Time` right? so you do not need the `*` in front of it. Additionally when converting to pointer types, ie `*T`, I would recommed to always put the pointer type in extra parentheses, ie `(*T)`. https://play.golang.com/p/jH9Q07sizID – mkopriva May 25 '19 at 20:03
  • That's very helpful, thank you! Could you perhaps explain further this syntax? I've seen it many times and I don't know even how to search for it in order to find documentation about it. Perhaps even a link to the proper place in http://tour.golang.org/ would do. – Doron Behar May 25 '19 at 20:45
  • 2
    https://golang.org/ref/spec#Conversions *If the type starts with the operator * or <-, or if the type starts with the keyword func and has no result list, it must be parenthesized when necessary to avoid ambiguity* – mkopriva May 25 '19 at 20:46
  • 2
    If you understand the concept of operator precedence, or order-of-evaluation, you can see that `*T(v)` could be interpreted in two ways and is therefore ambiguous, so to *avoid the ambiguity* you can make your intent clear by adding parentheses `(*T)(v)`. – mkopriva May 25 '19 at 20:54
  • That's an excellent explanation @mkopriva - exactly what I was missing. Please feel free to put these words into an answer I'll mark as accepted. – Doron Behar May 25 '19 at 21:00
  • Go doesn't have typecasting at all. – Jonathan Hall May 26 '19 at 08:56
  • Please take time to also absorb what Flimzy said: in Go, there is no type_casting_ — that is, forced reinterpretation of a value of one type as that of another. Instead, Go has type _conversions;_ which differ in that they ever allow to convert values between compatible types where compatible roughly meaning "representing values which have the same memory layouts for their data". – kostix May 27 '19 at 16:48
  • Thanks @kostix, I edited the title of the question. – Doron Behar May 28 '19 at 06:37

1 Answers1

5

The problem is the ambiguous syntax of *T(v) or whatever else you tried there. The Golang's spec gives useful examples for type conversions as this, quoting:

*Point(p)        // same as *(Point(p))
(*Point)(p)      // p is converted to *Point

Therefor, since *Point is needed, *T(v) should be used.

Doron Behar
  • 2,606
  • 2
  • 21
  • 24