7

Is it possible in go to get a "Type" without an instance? I've seen some examples that utilize reflect.TypeOf() but they all deal with an instance.

Below is a snippet of what I am attempting to do:

import (
    "net/http"
)

type ParamReader struct {
    // The request from which to extract parameters
    context *http.Request
}

// Initialize the ParamReader with a specific http request. This serves
// as the 'context' of our param reader. All subsequent calls will validate
// the params that are present on this assigned http.Request
func (p *ParamReader) Context(r *http.Request) {
    p.context = r
}

// Validate that a given param 's' is both present and a valid
// value of type 't'. A value is demeed valid if a conversion from 
// its string representation to 't' is possible
func(p *ParamReader) Require(s string, t Type) {
    // if context not have 's'
    //      addError('s' is not present)
    //      return


    if( t == typeof(uint64)) {
        // If not s -> uint64
        //      addError('s' is not a valid uint64)
    } else if (t == typeof(uint32)) {
        // ....
    } / ....
}

An example of my usage would be

func (h *Handler) OnRequest(r *http.Request) {
  h.ParamReader.Context(r)
  h.ParamReader.Require("age", uint16)
  h.ParamReader.Require("name", string)
  h.ParamReader.Require("coolfactor", uint64)
  h.ParamReader.Optional("email", string, "unspecified")
  h.ParamReader.Optional("money", uint64, "0")

  if h.ParamReader.HasErrors() {
    // Iterate or do something about the errors
  } else {
    coolness := h.ParamReader.ReadUint64("coolfactor")
    email := h.ParamReader.ReadString("email")
    money := h.ParamReader.ReadUint64(0)
  }
}

Note, after writing this out, I realize I could provide a "RequireUint64", "RequireUint32", etc.. perhaps that would be the Go way?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
bgura
  • 860
  • 15
  • 33
  • 1
    What about https://tour.golang.org/methods/16? – tkausl Feb 23 '18 at 01:04
  • 1
    @tkausl, that example is *almost* like what I am trying to accomplish. The only difference is that where they pass in ```"hello"``` to do, i'd want to pass in something like ```typeof(string)```. My use case is I don't actually have an instance as that example shows. – bgura Feb 23 '18 at 16:10

2 Answers2

17

Yes, it's possible. The trick is to start from a pointer to the type (whose value can be a typed nil, that's perfectly OK), and then use Type.Elem() to get the reflect.Type descriptor of the pointed type (the base type).

See some examples:

t := reflect.TypeOf((*int)(nil)).Elem()
fmt.Println(t)

t = reflect.TypeOf((*http.Request)(nil)).Elem()
fmt.Println(t)

t = reflect.TypeOf((*os.File)(nil)).Elem()
fmt.Println(t)

Output (try it on the Go Playground):

int
http.Request
os.File

See related questions:

Golang reflect: Get Type representation from name?

How to get the string representation of a type?

If you want to pass around the types and use them in switches, you can create and store them in global variables once like this, and refer to the global vars:

var (
    intType         = reflect.TypeOf((*int)(nil))
    httpRequestType = reflect.TypeOf((*http.Request)(nil))
    osFileType      = reflect.TypeOf((*os.File)(nil))
    int64Type       = reflect.TypeOf((*uint64)(nil))
)

func printType(t reflect.Type) {
    switch t {
    case intType:
        fmt.Println("Type: int")
    case httpRequestType:
        fmt.Println("Type: http.request")
    case osFileType:
        fmt.Println("Type: os.file")
    case int64Type:
        fmt.Println("Type: uint64")
    default:
        fmt.Println("Type: Other")
    }
}

func main() {
    printType(intType)
    printType(httpRequestType)
    printType(osFileType)
    printType(int64Type)
}

Output of the above (try it on the Go Playground):

Type: int
Type: http.request
Type: os.file
Type: uint64

But honestly, if you're using it like this way and you're not using reflect.Type's methods, then creating constants is much easier and more efficient. It could look like this:

type TypeDesc int

const (
    typeInt TypeDesc = iota
    typeHttpRequest
    typeOsFile
    typeInt64
)

func printType(t TypeDesc) {
    switch t {
    case typeInt:
        fmt.Println("Type: int")
    case typeHttpRequest:
        fmt.Println("Type: http.request")
    case typeOsFile:
        fmt.Println("Type: os.file")
    case typeInt64:
        fmt.Println("Type: uint64")
    default:
        fmt.Println("Type: Other")
    }
}

func main() {
    printType(typeInt)
    printType(typeHttpRequest)
    printType(typeOsFile)
    printType(typeInt64)
}

Output is the same. Try it on the Go Playground.

icza
  • 389,944
  • 63
  • 907
  • 827
  • It seems like this would be the way to go and I can use this to solve problem. Though i'm not entirely sure how to switch or do a comparison on ```Elem``` I modified your example to make it a closer representation of what I was shooting for: https://play.golang.org/p/aLyzzjjHgHD. I've chosen to pursue this route and wrap the ```reflect.TypeOf``` within a "more" readable and concise helper function & struct to make it explicit which "ParamTypes" are valid. – bgura Feb 23 '18 at 16:35
  • 1
    @bgura Added usage example. Also if you're going to use it like that, constants are more appropriate. See edited answer. – icza Feb 23 '18 at 16:44
  • a good point. This answer does now cover quite a variety of ways Go can solve the problem. – bgura Feb 23 '18 at 17:39
0

I'm fairly certain this isn't possible in Go. While it's far from ideal you can use Type.Name() and a string as the functions arg to do whatever comparisons are necessary.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    printIt("int")
}

func printIt(x string) {
    i := 10
    if (reflect.TypeOf(i).Name() == x) {
        fmt.Println(x)
    }
}
evanmcdonnal
  • 46,131
  • 16
  • 104
  • 115