Golang:给定常量值的名称

How do you get the name of a constant given its value ?

More specifically (and to get a more readable understanding), I'm working with the crypto/tls package. Cipher suites are defined as constants:

const (
    TLS_RSA_WITH_RC4_128_SHA            uint16 = 0x0005
    TLS_RSA_WITH_3DES_EDE_CBC_SHA       uint16 = 0x000a
    TLS_RSA_WITH_AES_128_CBC_SHA        uint16 = 0x002f
    TLS_RSA_WITH_AES_256_CBC_SHA        uint16 = 0x0035
    TLS_ECDHE_RSA_WITH_RC4_128_SHA      uint16 = 0xc011
    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA  uint16 = 0xc013
    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA  uint16 = 0xc014
)

After a successful handshake with a server, I can get to the Ciphersuite agreed on through the connection:

c, _ := tls.Dial("tcp", "somedomain.com:443", nil)
// Suppose everything went right

// This is the Ciphersuite for the conn:
cipher := c.ConnectionState().Ciphersuite

cipher here is an uint16. Is there a way to display it as a string, for instance TLS_RSA_WITH_3DES_EDE_CBC_SHA if that's what was agreed upon ?

I am afraid that is not possible.
The constants are resolved at compile time and there is nothing in the reflect package that allows you to retrieve the name.

What I suggest is creating a map with the names:

var constLookup = map[uint16]string{
    tls.TLS_RSA_WITH_RC4_128_SHA:      `TLS_RSA_WITH_RC4_128_SHA`,
    tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: `TLS_RSA_WITH_3DES_EDE_CBC_SHA`,
    ...
}

Note: As of Go 1.4, the String() code below can be auto-generated using Go's new generate feature, combined with the stringer command. See here for more info.

Apart from ANisus' answer, you can do the following.

package main

import "fmt"
import "crypto/tls"

type Ciphersuite uint16

const (
    TLS_RSA_WITH_RC4_128_SHA            = Ciphersuite(tls.TLS_RSA_WITH_RC4_128_SHA)
    TLS_RSA_WITH_3DES_EDE_CBC_SHA       = Ciphersuite(tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
    TLS_RSA_WITH_AES_128_CBC_SHA        = Ciphersuite(tls.TLS_RSA_WITH_AES_128_CBC_SHA)
    TLS_RSA_WITH_AES_256_CBC_SHA        = Ciphersuite(tls.TLS_RSA_WITH_AES_256_CBC_SHA)
    TLS_ECDHE_RSA_WITH_RC4_128_SHA      = Ciphersuite(tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA)
    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = Ciphersuite(tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA  = Ciphersuite(tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA  = Ciphersuite(tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
)

func (cs Ciphersuite) String() string {
    switch cs {
    case TLS_RSA_WITH_RC4_128_SHA:
        return "TLS_RSA_WITH_RC4_128_SHA"
    case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
        return "TLS_RSA_WITH_3DES_EDE_CBC_SHA"
    case TLS_RSA_WITH_AES_128_CBC_SHA:
        return "TLS_RSA_WITH_AES_128_CBC_SHA"
    case TLS_RSA_WITH_AES_256_CBC_SHA:
        return "TLS_RSA_WITH_AES_256_CBC_SHA"
    case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
        return "TLS_ECDHE_RSA_WITH_RC4_128_SHA"
    case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
        return "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"
    case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
        return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
    case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
        return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
    }
    return "Unknown"
}

func main() {
    cs := TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
    fmt.Printf("0x%04x = %s
", uint16(cs), cs)

    cs = TLS_RSA_WITH_RC4_128_SHA
    fmt.Printf("0x%04x = %s
", uint16(cs), cs)

    cs = TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
    fmt.Printf("0x%04x = %s
", uint16(cs), cs)
}

You can test it on the go playground.

You can use go generate to have this generated for you.

Install stringer using

go get golang.org/x/tools/cmd/stringer

Now define a type for your const. You can do it like this

type TLSType uint16
const (
    TLS_RSA_WITH_RC4_128_SHA            TLSType = 0x0005
    TLS_RSA_WITH_3DES_EDE_CBC_SHA       TLSType = 0x000a
    TLS_RSA_WITH_AES_128_CBC_SHA        TLSType = 0x002f
    TLS_RSA_WITH_AES_256_CBC_SHA        TLSType = 0x0035
    TLS_ECDHE_RSA_WITH_RC4_128_SHA      TLSType = 0xc011
    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA TLSType = 0xc012
    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA  TLSType = 0xc013
    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA  TLSType = 0xc014
)

Then add this line to your file (spacing is important)

//go:generate stringer -type=TLSType

Now go to the terminal in the folder where your program is and run

go generate

This will create a file called tlstype_string.go in your code directory, which is the generated code that implements the fmt.Stringer interface for your TLSType. Open it if you want to see the implementation but you don't need to. It will just work.

Now when you run go build or go run *.go on this directory, your source files will use the generated code.

The full program would look something like this

package main

import (
    "fmt"
)

//go:generate stringer -type=TLSType
type TLSType uint16

const (
    TLS_RSA_WITH_RC4_128_SHA            TLSType = 0x0005
    TLS_RSA_WITH_3DES_EDE_CBC_SHA       TLSType = 0x000a
    TLS_RSA_WITH_AES_128_CBC_SHA        TLSType = 0x002f
    TLS_RSA_WITH_AES_256_CBC_SHA        TLSType = 0x0035
    TLS_ECDHE_RSA_WITH_RC4_128_SHA      TLSType = 0xc011
    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA TLSType = 0xc012
    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA  TLSType = 0xc013
    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA  TLSType = 0xc014
)

func main() {
    fmt.Println("This const will be printed with its name instead of its number:")
    fmt.Println(TLS_RSA_WITH_RC4_128_SHA)
    fmt.Println()
    fmt.Println("So will this one:")
    fmt.Println(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
    fmt.Println()
    fmt.Println("Or maybe we want to give it a value and convert:")
    fmt.Println(TLSType(0xc011))
    fmt.Println()
    fmt.Println("No problem:")
    fmt.Println(TLSType(0xc013))
}

Which outputs

This const will be printed with its name instead of its number:
TLS_RSA_WITH_RC4_128_SHA

So will this one:
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA

Or maybe we want to give it a value and convert:
TLS_ECDHE_RSA_WITH_RC4_128_SHA

No problem:
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

If you add new const values, just run go generate again to update the generated code.