12

I have the following type defined using iota in Golang.

type StatusType int

const (
    PENDING StatusType = iota
    APPROVED
    REJECTED
)

I want to restrict the value passed in REST-API to the StatusType. Such that the value should not exceed 0,1,2.

wasmup
  • 9,237
  • 3
  • 27
  • 42
Shamsher
  • 1,571
  • 4
  • 19
  • 30
  • 2
    Detail not related to your question, but constants should not be uppercased (C style). It is Pending/Approved/Rejected – basgys May 28 '16 at 09:39
  • Possible duplicate of [Golang: Creating a Constant Type and Restricting the Type's Values](http://stackoverflow.com/questions/37385007/golang-creating-a-constant-type-and-restricting-the-types-values) – icza May 28 '16 at 16:32

6 Answers6

11

I do this way:
first create a package named "StatusType" (inside a folder named StatusType):
filename: $GOPATH/enum/StatusType/StatusType.go

package StatusType

type Int int

const (
    Pending Int = iota
    Approved
    Rejected
    end
)

func IsValid(value int) bool {
    return value < int(end)
}

and use like this ($GOPATH/enum/main.go):

package main

import (
    "enum/StatusType"
    "fmt"
)

func Test(enum StatusType.Int) {
    fmt.Println(enum) //1
}
func main() {
    Test(StatusType.Approved)

    fmt.Println(StatusType.IsValid(1))  //true
    fmt.Println(StatusType.IsValid(10)) //false
}

The StatusType package just exports what you need so there is no need to check against iota const range.
Just in case you want to check, use: StatusType.IsValid()
And nice thing about StatusType package is:
When you want function parameter of StatusType type use StatusType.Int and it reveals that it is enumeration of type int.
Like:

Test(StatusType.Approved)
9

Simply don't export StatusType (assuming you define it in package 'status').
This follow "What is an idiomatic way of representing enums in Go?":

type statusType int

const (
    PENDING statusType = iota
    APPROVED
    REJECTED
)
type StatusTyper interface {
    StatusType() statusType 
}

func(st statusType) StatusType() statusType {
    return st
}

Any external package would then refer to StatusType-like variable as status.PENDING, status.APPROVED or status.REJECTED.
(the only three statusType which implement the StatusTyper interface. Caveat applies.)

Community
  • 1
  • 1
VonC
  • 1,042,979
  • 435
  • 3,649
  • 4,283
  • 1
    Making `StatusType` unexported and using an interface won't cut it. See a dirty trick which I presented in [Golang: Creating a Constant Type and Restricting the Type's Values](http://stackoverflow.com/questions/37385007/golang-creating-a-constant-type-and-restricting-the-types-values) which allows to implement interfaces even with unexported methods. In fact, no constant type can be restricted this way. – icza May 28 '16 at 16:40
  • @icza Agree (and upvoted). I thought about unexported wrapper struct, but I want to stay as close as possible to the original code of the OP. As I say in the answer, "caveat applies". – VonC May 28 '16 at 17:22
3

Assuming you wish for invalid JSON payloads to fail, implement the Unmarshaler interface: https://play.golang.org/p/zuchzQ0vmo

Martin Gallagher
  • 3,284
  • 1
  • 23
  • 24
0

use go generate with github.com/alvaroloes/enumer

package main

import "fmt"

//go:generate enumer -type=StatusType
type StatusType int

const (
    PENDING StatusType = iota
    APPROVED
    REJECTED
)

func main() {
    fmt.Println(StatusType(0).IsAStatusType()) // true
    fmt.Println(StatusType(1).IsAStatusType()) // true
    fmt.Println(StatusType(2).IsAStatusType()) // true
    fmt.Println(StatusType(3).IsAStatusType()) // false
}
0

the iota is merely a compiler thing. The code is equivalent to:

const PENDING int = 0
const APPROVED int = 1
...

So to design a function CheckValid() to decide if the value is among the given values. You could either use user6169399's method if your const is in a continuous range. Or you could simply define a var map[YOUR_TYPE_HERE]bool to validate.

func (t YOUR_TYPE) CheckValid(){
    if _, ok:=map[t];ok return true
    else return false
}
Keiser He
  • 11
  • 2
0

Here are another two ways to do it right without map https://play.golang.org/p/eKW_KPshx7b

package main

import (
    "errors"
    "log"
)

type StatusType int

const (
    PENDING StatusType = iota
    APPROVED
    REJECTED
)

func Validate(val int) (bool, error) {
    if v := StatusType(val); v > REJECTED || v < PENDING {
        return false, errors.New("invalid StatusType")
    }
    return true, nil
}

func (t StatusType) Validate() (bool, error) {
    if t > REJECTED || t < PENDING {
        return false, errors.New("invalid StatusType")
    }
    return true, nil
}


func main() {
    log.Print(Validate(-1))
    log.Print(Validate(0))
    log.Print(Validate(1))
    log.Print(Validate(3))

    log.Print(StatusType(-1).Validate())
    log.Print(StatusType(1).Validate())
    log.Print(StatusType(10).Validate())
}