The following example defines a series of port numbers starting at 3333 using iota.
package main
import (
"fmt"
)
const (
FirstPort = iota+3333
SecondPort
ThirdPort
)
func main() {
hostAndPort := "localhost:"+fmt.Sprint(SecondPort)
fmt.Printf("%s", hostAndPort )
// Output:
// localhost:3334
}
When combining hostname and ports, I'd like to avoid having to wrap the port constant in fmt.Sprint
and simply write, for example, "localhost:"+SecondPort
. Is there a way to use iota to define the port numbers as string constants, e.g "3334"
?
The following doesn't work:
FirstPort = string(iota + 3333)
Neither does
FirstPort = fmt.Sprintf("%d", iota + 3333)
Quoting from Spec: Iota:
Within a constant declaration, the predeclared identifier
iota
represents successive untyped integer constants.
So iota
provides you integer constants. If we want string
constants, we need to find a way to convert an integer to its base-10 string
representation. This way must be a constant expression, else we can't use it in a constant declaration.
Unfortunately for us, a simple type conversion from integer to string
will not yield the base-10 representation of the numerical value, but:
Converting a signed or unsigned integer value to a string type yields a string containing the UTF-8 representation of the integer.
So the result will be a string
holding a single rune, whose value (the Unicode codepoint) is the source number.
Also calling "converter" functions such as strconv.Itoa()
or fmt.Sprint()
is out of the question, as calling those functions cannot be part of a constant expression, so the result could only be used in a variable declaration (not to mention we couldn't use iota
, it's only allowed in constant declarations).
But there is still a solution.
I don't think it is worth the hassle and the loss of readability, but actually you can define string
constants holding increasing decimal numbers using iota
.
The solution builds the "complete" numbers from digits. We can obtain the base-10 string
representation by concatenating the digits (as string
values) of the number.
Last question to solve for this is how to "list" the digits of a number. This is simple arithmetic:
i % 10
.i / 10 % 10
.i / 100 % 10
.And to obtain the rune
for a digit (which is in the range of 0..9
), we can simply add '0'
to it, and convert it to string
. And that's all.
This is how we can code this for a 1-digit string number:
n0 = string('0'+iota%10)
For a 2-digit number:
n00 = string('0'+iota/10%10) + string('0'+iota/1%10)
For a 3-digit number:
n000 = string('0'+iota/100%10) + string('0'+iota/10%10) + string('0'+iota/1%10)
Let's see it in action:
const (
P00 = string('0'+iota/10%10) + string('0'+iota/1%10)
P01
P02
P03
P04
P05
P06
P07
P08
P09
P10
P11
P12
P13
P14
P15
P16
P17
P18
P19
P20
)
Printing the results:
fmt.Printf("%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n%v\n",
P00, P01, P02, P03, P04, P05, P06, P07, P08, P09,
P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20)
Output (try it on the Go Playground):
00
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
So far so good, but how do we make it start at 3333
?
Also not a problem, can be achieved easily. We can shift the iota, simply by adding an "initial" number to it. And that's all it takes.
Let's see an example where the first number will be 3339
:
const (
P3339 = string('0'+(iota+3339)/1000%10) +
string('0'+(iota+3339)/100%10) +
string('0'+(iota+3339)/10%10) +
string('0'+(iota+3339)/1%10)
P3340
P3341
)
func main() {
fmt.Println(P3339)
fmt.Println(P3340)
fmt.Println(P3341)
}
Output of the above is the expected (try it on the Go Playground):
3339
3340
3341