Variable with a predefined list of options

Hi all, I want to have a variable which will only have one of the three values: 1, 3, or both. I am looking for a way to implement this go. Is there any go specific way to implement this? Thank you!

Can we get a bit more info on your use case? If you really need a variable that can only be in one of those 3 states, you could do this: https://play.golang.org/p/vowJmrzr1Ml, but I personally think this code is clunky. It will become more clunky if you want to handle (un)marshaling. I would probably have some validation logic somewhere (web service? validator on a UI field? It depends on context) to handle checking the value to make sure it’s one of 3 options instead of building it into the type unless this type is used throughout the codebase.

1 Like

I can think of a few ways I might go about this. I was going to type up a response using enums but @skillian did a great job covering that. For other options, you could just declare a simple type around int and have constants to indicate your values, but this isn’t safe:

type OneOrThree int

const (
	One   OneOrThree = 1
	Three OneOrThree = 3
	Both  OneOrThree = 4
)

func main() {
	// Developer knows what's up!
	doSomething(One)
	doSomething(Three)
	doSomething(Both)
	// Yikes...
	doSomething(5)
}

func doSomething(arg OneOrThree) {
	fmt.Println(arg)
}

https://play.golang.org/p/vgwjKisCDee

However, for internal use this approach has been fine in my experience because the intent is pretty clear. Where is this value coming from? Is it coming from an external source like something posting to an API? Is your function exported? Those questions might drive your design. If you want safety and/or this is something being exported, a simple option is to validate an int array:

// DoSomething accepts an array with the values 1, 3, or both. 
// Will return an error if argument is out of range.
func DoSomething(arg []int) error {
	if len(arg) == 0 {
		return errors.New("arg must contain a value")
	}
	if len(arg) > 2 {
		return errors.New("arg can only contain two values")
	}
	for _, v := range arg {
		if v != 1 && v != 3 {
			return errors.New("arg can only contain 1 or 3")
		}
	}
	fmt.Println("OK value:", arg)
	return nil
}

https://play.golang.org/p/vQiKhWX-7Il

Anyway, it’s hard to say more without knowing more about your use case. But hopefully this gives you some ideas.

See also:

What you are looking for is called enums. Go does not have enums.

Instead what we do is create a type t and define a set of global t variables s. It is then the programmers responsibility to only pass values in s as t parameters.

Yeah… :confused:

Ironically, go is listed on the very wikipedia page you linked to:

Go uses the iota keyword to create enumerated constants

That is indeed ironical.

In computer programming, an enumerated type (also called enumeration, enum, or factor in the R programming language, and a categorical variable in statistics) is a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type.

a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type.

Go does not have this. They are contradicting themselves by listing Go.

Maybe you want to use regex, but need convert to string and import strconv… :blush:
Please, more information what you want!!

package main

import (
	"fmt"
	"regexp"
	"strconv"
)

func main() {

	arg := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}

	for _, v := range arg {

		// convert to string
		s := strconv.Itoa(v)

		// create a regex expression
		var myRegex = regexp.MustCompile("^[1|3]$|^13$")

		// return your regex test
		fmt.Println(myRegex.MatchString(s))
		// You can use if, switch to check return is true
	}
}

https://play.golang.org/p/_dd8KmZvqh6

I’m not sure what you guys mean about Go not having enumerated constants. They don’t work like, for example, Java’s enums where they cannot be cast to/from ints, but they do work somewhat like enums in C, C++ and C#.

Here’s an example in C where you can implicitly cast (or is it “convert?” I’m not a language expert) an integer constant to an enum: https://cplayground.com/?p=dog-fly-caribou In C++ and C# you can do the same thing, but you have to explicitly cast it.

Go is like that too: You can assign integer constants to an "enum typed" variable like you can in C because of how Go constants work but then you can’t take a non-const int value and assign that to a Go "enum typed" variable without a cast.

I’d recommend Dean_Davidson’s suggestion of the OneOrThree "enum type" unless you have a need to validate that the state of a variable is in one of n possible states at the time it’s assigned, but if that’s the case, I’d be interested in seeing the scenario.

I was actually trying to point out that Go does indeed have enumerated constants. The syntactic sugar is different depending on the language, and certain languages like Rust offer completely new interpretations of what an enum is. I came to go by way of .NET for example, and here’s an example of OneOrThree in C#:

using System;

enum OneOrThree
{
	One = 1,
	Three = 3,
	Both = 4
}

public class Program
{
	public static void Main()
	{
		// All good
		doSomething(OneOrThree.One);
		// Also fine
		var both = (OneOrThree)4;
		doSomething(both);
		// Outputs 5
		var bad = (OneOrThree)5;
		doSomething(bad);
	}

	static void doSomething(OneOrThree arg)
	{
		Console.WriteLine(arg);
	}
}

https://dotnetfiddle.net/AwIzXA

Again, the ergonomics are slightly different, but it’s pretty similar to the implementation in go, and both are storing the underlying values as int constants by default:

By default, the associated constant values of enum members are of type int ; they start with zero and increase by one following the definition text order. You can explicitly specify any other integral numeric type as an underlying type of an enumeration type.

The main difference is that in .NET enums are actually a class that inherits from System.Enum. So, I guess if you wanted the functionality to be more similar you could declare a separate package for your enum - but that seems superfluous to me.

Hi all, Thank you very much for your detailed responses. They give extensive ideas to learn and build over. My scenario is like a seller will set the duration of a license purchase from his UI. Then buyers are only presented with those options like, if a seller can enable both options. So the buyer can buy it for either 1 year or 3 years period. If the seller chooses only one option, then the buyer can only buy the license for that duration. I will go through all of your implementations and understand them.

Thank you very much.