I am trying to use enum in golang as below. I am struggling to find a easy way to iterate over the list of constant values. What are common practice in golang to iterate over constant values used as enum. Thanks!
type DayOfWeek int
const(
Monday DayOfWeek = iota
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
In Java, we can iterate as below.
public enum DayOfWeek {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
for (DayOfWeek day: DayOfWeek.values()) {
// code logic
}
There is no direct way to enumerate the values/instances of named type at runtime, whether variables or constants, unless you specifically define a slice that lists them. This is left up to the definer or the user of the enumeration type.
package main
import (
"fmt"
"time"
)
var Weekdays = []time.Weekday{
time.Sunday,
time.Monday,
time.Tuesday,
time.Wednesday,
time.Thursday,
time.Friday,
time.Saturday,
}
func main() {
for _, day := range Weekdays {
fmt.Println(day)
}
}
In order be able to generate this list dynamically at runtime, e.g. via reflection, the linker would have to retain all the symbols defined in all packages, like Java does. The golang-nuts group discussed this, regarding names and functions exported from a package, a superset of package constant definitions. https://groups.google.com/forum/#!topic/golang-nuts/M0ORoEU115o
It would be possible for the language to include syntactic sugar for generating this list at compile time if and only if it were referenced by the program. What should the iteration order be, though? If your week starts on Monday the list I defined is not very helpful; you will have to define your own slice to range through the days from Monday to Sunday.
You can do that without reflection.
First execute the Go tools Stringer at compile time using go generate. This creates a file [filename]_string.go which contains a map _[structname]_map of enum values referencing enum variable names as strings. This map is private, so simply assign it to a public map upon package initialization.
var EnumMap map[Enum]string
func init() {
EnumMap = _Enum_map
}
type Enum uint
//go:generate go run golang.org/x/tools/cmd/stringer -type=Enum
const (
One Enum = iota
Two
)
Then you can simply loop over the keys of the map.
The comment from #davec was great. This works perfect when you have a count that increments by one.
You could either do a simple loop such as for d := Monday; d <= Sunday; d++ {}
I had constant that jumped in bits (1,2,4,8,16 etc):
const (
Approved = 1 << iota
AlreadyApproved
NotApproved
OldTicket
Unknown
)
I avoided range and did a left shift one to move through my constant:
var score Bits
score = Set(score, AlreadyApproved)
for i := Approved; i < Unknown; i = i << 1 {
fmt.Println(i)
}
Output:
1
2
4
8
16
Using stringer is preferable it can help you keep your codebase up to date using code generators. Unfortunately stringer does not always generate the map.
For anyone interested in keep using go generators for this purpose I wrote a small code generator called enumall. It produces a file for each provided type with variable holding all values for given type.
Use it by adding code generator comment to your code like this:
//go:generate go run github.com/tomaspavlic/enumall#latest -type=Season
type Season uint8
const (
Spring Season = 1 << iota
Summer
Autumn
Winter
)
You can find more information here: https://github.com/tomaspavlic/enumall
Related
This question already has an answer here:
Generic Structs with Go
(1 answer)
Closed 8 months ago.
I try to run examples from the design draft (The Next Step for Generics) on
go2go.playground
type Pair(type T) struct { f1, f2 T }
, but get an error
prog.go2:14:11: expected type, found 'type' (and 1 more errors)
Where can I find actual go generics design draft?
You're looking at outdated design draft.
This one is current: https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md
So, it's not:
type Pair(type T) struct { f1, f2 T }
But:
type Pair[T any] struct { f1, f2 T }
That's old syntax since a few weeks ago. Try
type Pair[T any] struct { f1, f2 T }
Note that square brackets are now used instead of round brackets and the type keyword is no longer used. You also must use the any constraint, whereas previously you could leave the constraint out if there was no restriction on the type parameter.
BTW Conventionally Pair refers to a struct with 2 different types for the 2 fields like type Pair[T1, T2 any] struct { first T1; second T2 }
See go2go Playground for example code that builds.
As mentioned in the first paragraph of the 2019 design draft the new draft is https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md
I'm trying to build a new list of structs that contains references to items that exist in another slice. It's easier to understand if you see it, so I've prepared a snippet that you can run.
I have a list (dummylist) of two points (Cartesian coordinates) that I want to parse to build a new list (mylist) with items having some features (in the example, X > 80). I've defined two points: {X:90.0, Y:50.0} and {X:20.0 , Y:30.0}. I expect that mylist will contain {X:90.0, Y:50.0}, instead at the end there is {X:20.0 , Y:30.0}. With some print here and there I can verify that the algorithm is working fine (it enters in the "if" condition in the right case), but, at the end, "mylist" contains the wrong element.
package main
import(
"fmt"
)
func main() {
type point struct {
X float64
Y float64
}
type pointsList []point
type pointContainer struct {
Point *point
}
type pointContainerList []pointContainer
// Prepare a slice with two elements
dummylist := new(pointsList)
*dummylist = append(*dummylist, point{X:90.0, Y:50.0})
*dummylist = append(*dummylist, point{X:20.0 , Y:30.0})
// My empty list
mylist := new(pointContainerList)
fmt.Println(fmt.Sprintf("---- At the beginning, mylist contains %d points", len(*mylist)))
// Filter the initial list to take only elements
for _, pt := range *dummylist {
fmt.Println("\n---- Evaluating point ", pt)
if pt.X > 80 {
fmt.Println("Appending", pt)
*mylist = append(*mylist, pointContainer{Point: &pt})
fmt.Println("Inserted point:", (*mylist)[0].Point, "len = ", len(*mylist))
}
}
// mylist should contain {X:90.0, Y:50.0}, instead...
fmt.Println(fmt.Sprintf("\n---- At the end, mylist contains %d points", len(*mylist)))
fmt.Println("Content of mylist:", (*mylist)[0].Point)
}
Here you can run the code:
https://play.golang.org/p/AvrC3JJBLdT
Some helpful consideration:
I've seen through multiple tests that, at the end, mylist contains the last parsed item in the loop. I think there is a problem with references. It's like if the inserted item in the list (in the first iteration) is dependent on the "pt" of other iterations. Instead, if I use indexes (for i, pt := range *dummylist and (*dummylist)[i]), everything works fine.
Before talking about bugs in Golang... am I missing something?
Yes, you're missing something. On this line:
*mylist = append(*mylist, pointContainer{Point: &pt})
you're putting the address of the loop variable &pt into your structure. As the loop continues, the value of pt changes. (Or to put it another way, &pt will be the same pointer for each iteration of the loop).
From the go language specification:
...
The iteration values are assigned to the respective iteration
variables as in an assignment statement.
The iteration variables may be declared by the "range" clause using a
form of short variable declaration (:=). In this case their types are
set to the types of the respective iteration values and their scope is
the block of the "for" statement; they are re-used in each iteration.
If the iteration variables are declared outside the "for" statement,
after execution their values will be those of the last iteration.
One solution would be to create a new value, but I'm not sure what you're gaining from so many pointers: []point would probably be more effective (and less error-prone) than a pointer to a slice of structs of pointers to points.
In Go we can say:
type Month int
to create a new type based off of int.
Is it possible to also say that the range of values allowed for this type is 1 - 12, and to guarantee that no value < 1 or > 12 can be assigned?
No, you cannot put limits on an int whether you define it as a custom type or not. The closest you can get is something like the following code using a construct called iota
type Month int
const (
Jan Month = iota + 1
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
)
func main() {
fmt.Println(Jan, Feb, Mar)
}
This will print 1 2 3. There are a lot more uses for it, you can look up more information here https://splice.com/blog/iota-elegant-constants-golang/
This does not guarantee that you cannot assign random values to the resulting variable, but as long as you use the defined constants everywhere you should be fine.
You can limit access to a single package by using an unexported variable to store the value. For example,
package date
type Month struct {
month int
}
This question already has answers here:
Why are string functions not defined on the string type?
(2 answers)
Closed 4 years ago.
Here are some snippets when you want to split a string with some specific delimiter in different languages:
# python
s = 'a,b,c,d,e'
tokens = s.split(',')
// javascript
let s = 'a,b,c,d,e'
let tokens = s.split(',')
// go
s := "a,b,c,d,e"
tokens := strings.Split(s, ",")
As you can see, "split" is a member function of type string in Python and Javascript, but not in Go.
I am wondering why is that, it seems like STL in CPP, why the functions to manipulate an instance of a type are not member functions of that type, it seems easy to implement them in Go, like:
// go
func (s *string) Split(d string) []string {
// here goes the code to split s with d given
}
what is the reason it is designed this way?
As you can see, "split" is a member function of type string in python and javascript, but not in golang.
That seems to have been so from the very beginning: commit 729bc5c, Sept 2008, for Go1 is the first commit with any mention of a string Split() function.
rudimentary string utilities.
Those functions were considered "utilities", and not part of the predeclared string type 'string' itself.
It was documented soon after in commit 0f7306b, March 2009, still Go1
// Split returns the array representing the substrings of s separated by string sep. Adjacent
// occurrences of sep produce empty substrings. If sep is empty, it is the same as Explode.
func Split(s, sep string) []string {
You can see it used for the first time in commit 5eae3b2, April 2009 in func LookPath(file string) (string, *os.Error) {
The same approach was use for byte with bytes: commit 7893322, June 2009; Go1, with a similar Split() function.
add a bytes package analogous to the strings package.
The general idea is: you can change that utility function without changing the value type itself.
See commit 30533d6, June 2009:
Change strings.Split, bytes.Split to take a maximum substring count argument.
func Split(s, sep []byte, n int) [][]byte
An even more drastic evolution:commit ebb1566, June 2011
strings.Split: make the default to split all.
Change the signature of Split to have no count, assuming a full split, and rename the existing Split with a count to SplitN.
The other idea is to keep using string, while possibly removing dependencies to those utility functions when you don't need them (as in commit 35ace1d, Nov. 2009: "remove dependencies on strconv and strings")
It also allows to add more related function, without touching string itself.
See commit 5d436b9, Nov. 2009: lines := strings.SplitAfter(text, "\n", 0), which uses Split().
Another advantage: you can optimize those functions independently of string itself, allowing for duplicate 'Split' function to be replaced by strings.Split().
See commit f388119, March 2013, Go 1.1
go/printer: use strings.Split instead of specialized code
With the faster strings package, the difference between the specialized code and strings.Split is in the noise:
benchmark old ns/op new ns/op delta
BenchmarkPrint 16724291 16686729 -0.22%
The opposite case is also true: replacing strings.Split by a simpler code, as in commit d0c9b40, Sept. 2015, Go 1.6
mime: Remove an allocation in word decoding.
This fixes a TODO in (*WordDecoder).Decode by replacing a call to strings.Split with simple prefix/suffix checking and some custom slicing.
Benchmark results:
benchmark old ns/op new ns/op delta
BenchmarkQEncodeWord-8 740 693 -6.35%
BenchmarkQDecodeWord-8 1291 727 -43.69%
BenchmarkQDecodeHeader-8 1194 767 -35.76%
(same idea in commit ecff943, Sept. 2017, Go 1.11)
I am having problem understanding how Random generators were suppose to work.
In the following example I'm trying to roll two dices on click,
https://ellie-app.com/d9rXQHpfJa1/1
but 'm getting
Function generate is expecting the 2nd argument to be:
Random.Generator ( Int, Int )
But it is:
( Random.Generator Int, Random.Generator Int )
The error message is giving you a strong hint that you are attempting to pass in a parameter that doesn't fit with what is expected. Elm does a lot of type inference and since NewFace (Int, Int) is a constructor that takes a tuple of two integers, that's what it expects for the second parameter passed to generate.
If you visit the Random package documentation, you'll see a function that takes two generators and gives you back a generator that uses the first two in a tuple. It is called Random.pair, and if you were to use it in your code, it would look like this:
( model, Random.generate NewFace (Random.pair (Random.int 1 6) (Random.int 1 6 )))