Michael Ben-Nes
Michael Ben-Nes

Reputation: 7645

Passing time.Time as parameter to generic function

Trying to use generics with time.Time. The general goal was to accept time.Time or milliseconds as int64

This code return the error:

Cannot use 'at' (type T) as the type time.Time

type SomeTime struct {
    At        time.Time
}

func PointFactory[T time.Time](at T) *SomeTime {
    return &SomeTime{
        At:        at,
    }
}

Is it possible to pass time.Time as parameter to generic function? Thanks

Upvotes: 0

Views: 375

Answers (4)

Fenistil
Fenistil

Reputation: 3801

The closest you can get is to cast your T param to any then use a type switch to make the desired structs:

package main

import (
    "fmt"
    "time"
)

type SomeTime struct {
    At time.Time
}

func PointFactory[T time.Time | int64](at T) *SomeTime {
    switch any(at).(type) {
    case time.Time:
        fmt.Println("time")
        return &SomeTime{
            At: v,
        }

    case int64:
        fmt.Println("int")
        return &SomeTime{
            At: time.Unix(v, 0),
        }

    }
    return nil

}

func main() {
    fmt.Println(PointFactory(int64(6)))
    fmt.Println(PointFactory(time.Now()))
}

PlayGround

Output:

int
&{1970-01-01 00:00:06 +0000 UTC}
time
&{2009-11-10 23:00:00 +0000 UTC m=+0.000000001}

This is because the decision of the Go team:

In an earlier version of this design, we permitted using type assertions and type switches on variables whose type was a type parameter, or whose type was based on a type parameter. We removed this facility because it is always possible to convert a value of any type to the empty interface type, and then use a type assertion or type switch on that. Also, it was sometimes confusing that in a constraint with a type set that uses approximation elements, a type assertion or type switch would use the actual type argument, not the underlying type of the type argument (the difference is explained in the section on identifying the matched predeclared type)

Upvotes: 3

Mihai
Mihai

Reputation: 10757

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Printf("%v\n", PointFactory(time.Now()).At)
    fmt.Printf("%v\n", PointFactory(time.Now().Unix()).At)
}

type SomeTime[T time.Time | int64] struct {
    At T
}

func PointFactory[T time.Time | int64](at T) *SomeTime[T] {
    return &SomeTime[T]{
        At: at,
    }
}

Upvotes: 0

user19812413
user19812413

Reputation:

If the Sometime struct is expected to accept time.Time or int64 then it needs to declare it, the methods set the value to At what type is available

type SomeTime[T time.Time | int64] struct {
    At T
}

func PointFactoryTime(at time.Time) *SomeTime[time.Time] {
    return &SomeTime[time.Time]{
        At: at,
    }
}

func PointFactoryInt64(at int64) *SomeTime[int64] {
    return &SomeTime[int64]{
        At: at,
    }
}

Upvotes: 0

Ado Ren
Ado Ren

Reputation: 4404

You may declare multiple type as such:

func say[T string | int](msg T) {
    fmt.Println(msg)
}

func main() {
    say(1)
    say("hello")
}

See playground

Now as per your question, may you redefine your functions ? You may use:

PointFromInt(n int) *SomeTime {...}
PointFromTime(t time.Time) *SomeTime {...}

Your usecase may not be a good candidate for generics. Or regular typed function may do a better job.

Upvotes: 1

Related Questions