Lion.k
Lion.k

Reputation: 2769

How to pass type to function argument in Go

ERROR: type CustomStruct is not an expression.

type CustomStruct struct {
}

func getTypeName(t interface{}) string {
    rt := reflect.TypeOf(t).Elem()
    return rt.Name()
}

getTypeName(CustomStruct)

How can I pass struct type to function without type instance?

This will work

getTypeName((*CustomStruct)(nil))

But I wonder if there is more simple version..

Upvotes: 36

Views: 59016

Answers (3)

Shahriar Ahmed
Shahriar Ahmed

Reputation: 625

From Go version 1.18 a new feature Generics has been introduced. In most of the case instead of passing types to function, we can use generics. Then we will also get compile time error instead of runtime error and it's more efficient than reflect also.

Example Code

func HttpGet[T](url, body) T {
    var resp T
    return T
}

resp := HttpGet[ResponseType]("dummy.example", nil)

Upvotes: 4

Sam Hughes
Sam Hughes

Reputation: 785

Lets resurrect this!

The generics proposal for Go got approved, and that's coming, eventually. When this question was first asked, this probably made more sense as a question, but for anyone looking to implement a generics pattern now, I think I've got an alright API for it.

For now, you can't interact with abstract types, but you can interact with methods on the abstract type, and reflect allows you to examine function signatures. For a method, the 0th is the receiver.

type Example struct {int}
type Generic struct{reflect.Type}

func (p Example) Type() {}

func Reflect(generic interface{}) Generic {
    real := reflect.TypeOf(generic)
    if real.Kind() != reflect.Func || real.NumIn() < 1 {
        panic("reflect.Type.In(n) panics if not a func and if n out of bounds")
    }
    return Generic{real.In(0)}
}

func (g Generic) Make() interface{} {
    return reflect.Zero(g.Type).Interface()
}

func main() {
    tOfp := Reflect(Example.Type)
    fmt.Printf("Name of the type: %v\n", tOfp.Name()) 
    fmt.Printf("Real (initial)value: %v\n", tOfp.Make())
}

Some quick notes:

  1. The structure of "Example" doesn't matter, rather only that it has a method with a non-pointer receiver.
  2. The definition of a type called "Generic" as a struct is to accomplish what I believed OP's actual intent to be.
  3. The above definition of "Generic" is a struct instead of an interface so that it can have its own method set. Defining "Generic" as an interface, and using a methodset specific to each operand-type used with it would make tons of sense.

If you weren't aware, actual generics are coming in Go 1.18. My example above has no linter or compile protection, and will panic at runtime if used incorrectly. It does work, and will let you reason over abstract types while you wait for a native implementation.

Happy Coding!

Upvotes: 12

icza
icza

Reputation: 418445

You can't. You can only pass a value, and CustomStruct is not a value but a type. Using a type identifier is a compile-time error.

Usually when a "type" is to be passed, you pass a reflect.Type value which describes the type. This is what you "create" inside your getTypeName(), but then the getTypeName() will have little left to do:

func getTypeName(t reflect.Type) string {
    return t.Name()
}

// Calling it:
getTypeName(reflect.TypeOf(CustomStruct{}))

(Also don't forget that this returns an empty string for anonymous types such as []int.)

Another way is to pass a "typed" nil pointer value as you did, but again, you can just as well use a typed nil value to create the reflect.Type too, without creating a value of the type in question, like this:

t := reflect.TypeOf((*CustomStruct)(nil)).Elem()
fmt.Println(t.Name()) // Prints CustomStruct

Upvotes: 44

Related Questions