Can you declare multiple variables at once in Go? - go

Is it possible to declare multiple variables at once using Golang?
For example in Python you can type this:
a = b = c = 80
and all values will be 80.

Yes, you can:
var a, b, c string
a = "foo"
fmt.Println(a)
You can do something sort of similar for inline assignment, but not quite as convenient:
a, b, c := 80, 80, 80

Another way to do this is like this
var (
a = 12
b = 3
enableFeatureA = false
foo = "bar"
myvar float64
anothervar float64 = 2.4
)
Also works for const
const (
xconst = 5
boolconst = false
)

In terms of language specification, this is because the variables are defined with:
VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
(From "Variable declaration")
A list of identifiers for one type, assigned to one expression or ExpressionList.
const a, b, c = 3, 4, "foo" // a = 3, b = 4, c = "foo", untyped integer and string constants
const u, v float32 = 0, 3 // u = 0.0, v = 3.0

Yes you can and it is slightly more nuanced than it seems.
To start with, you can do something as plain as:
var a, b, x, y int // declares four variables all of type int
You can use the same syntax in function parameter declarations:
func foo(a, b string) { // takes two string parameters a and b
...
}
Then comes the short-hand syntax for declaring and assigning a variable at the same time.
x, y := "Hello", 10 // x is an instance of `string`, y is of type `int`
An oft-encountered pattern in Golang is:
result, err := some_api(...) // declares and sets `result` and `err`
if err != nil {
// ...
return err
}
result1, err := some_other_api(...) // declares and sets `result1`, reassigns `err`
if err != nil {
return err
}
So you can assign to already-defined variables on the left side of the := operator, so long as at least one of the variables being assigned to is new. Otherwise it's not well-formed. This is nifty because it allows us to reuse the same error variable for multiple API calls, instead of having to define a new one for each API call. But guard against inadvertent use of the following:
result, err := some_api(...) // declares and sets `result` and `err`
if err != nil {
// ...
return err
}
if result1, err := some_other_api(...); err != nil { // result1, err are both created afresh,
// visible only in the scope of this block.
// this err shadows err from outer block
return err
}

Several answers are incorrect: they ignore the fact that the OP is asking whether it is possible to set several variables to the same value in one go (sorry for the pun).
In go, it seems you cannot if a, b, c are variables, ie you will have to set each variable individually:
a, b, c := 80, 80, 80
But if a, b, c are constants, you can:
const (
a = 80
b
c
)

Try this in the go-playground:
https://play.golang.org/
package main
import "fmt"
func main() {
a, b := "a", "b"; //Declare And Assign
var c, d string; //Declare Only
fmt.Println(a,b);
fmt.Println(c,d);
}

Another way of doing is using var for package level assignment
package main
import (
"fmt"
)
var (
a, b, c = 80, 80 ,80
)
func main() {
fmt.Println(a, b, c)
}

long declaration
var varName1, varName2 string = "value","value"
short declaration
varName1,varName2 := "value1","value2"

Related

Is it possible to infer type parameters from what return values are assigned to?

Suppose I wrote two functions like this:
func ToInterfaceSlice[T any](s []T) []interface{} {
res := make([]interface{}, len(s))
for i, v := range s {
res[i] = v
}
return res
}
func FromInterfaceSlice[T any](s []interface{}) (res []T, err error) {
res = make([]T, len(s))
for i, v := range s {
vt, ok := v.(T)
if !ok {
return nil, fmt.Errorf("%v (type=%T) doesn't fit the target type %T", v, v, res)
}
res[i] = vt
}
return
}
When I parse type from the input parameters, I can simply use
var m = []int{1, 2, 3}
fmt.Println(ToInterfaceSlice(m))
The compiler knows the T is int.
However when I try passing type from the return variables
var m []int
m, _ = FromInterfaceSlice([]interface{}{1, 2, 3})
fmt.Println(m)
The compiler gives error:
.\scratch.go:29:27: cannot infer T
I must explicitly pass the type in the function call:
var m []int
m, _ = FromInterfaceSlice[int]([]interface{}{1, 2, 3})
fmt.Println(m)
Is there anything hard to infer type parameters from return type when the receiver vars are not interface? Or just not implemented, even not to implement on purpose?
Update #1 after the comment
I do know a, b := GenericFunc() cannot refer the type of returned value. Currently Go does have "it depends" case whether requires the explicit instantiation or not from the user input.
type Set[T comparable] map[T]struct{}
func NewSet[T comparable](eles ...T) Set[T] {
s := make(Set[T])
for _, ele := range eles {
s[ele] = struct{}{}
}
return s
}
It's okay to use both t := NewSet(1, 2, 3) and t := NewSet[string](), but not var t NewSet[float64] = NewSet() now because of this
The current rules for type inference are explicit. How the return values are used is not taken into account:
Type inference is based on
a type parameter list
a substitution map M initialized with the known type arguments, if any
a (possibly empty) list of ordinary function arguments (in case of a function call only)
As of Go 1.18 might simply rewrite your function to accept an argument of the required type; this has also the benefit of not hiding allocations inside the function body:
func FromInterfaceSlice[T any](s []interface{}, dst []T) error {
if len(s) != len(dst) {
return errors.New("lengths don't match")
}
for i, v := range s {
vt, ok := v.(T)
if !ok {
return nil, fmt.Errorf("%v (type=%T) doesn't fit the target type %T", v, v, res)
}
dst[i] = vt
}
return nil
}
And pass in a destination slice with the required length:
func main() {
src := []interface{}{1, 2, 3}
m := make([]int, len(src))
_ = FromInterfaceSlice(src, m)
fmt.Println(m)
}
If you can't or don't want to determine the slice's length beforehand, you are left with explicit instantiation:
var m []int
m, _ = FromInterfaceSlice[int]([]interface{}{1, 2, 3})
// ^^^ explicit type argument
Also the type parameters are still not inferrable with := shorthand declaration:
// what is m???
m, err := FromInterfaceSlice([]interface{}{1, 2, 3})

Go: best practice for safely converting integers to protocol buffer enum values

I have an enum in a proto file that generates to integer constants in the pb.go file. I now have some integers coming from the an external data source and want to safely map them to the possible constants.
Here is what I currently have: https://play.golang.org/p/-5VZqPbukd
package main
import (
"errors"
"fmt"
)
//enum in the proto file
//
// enum X {
// A = 0;
// B = 1;
// C = 2;
// D = 3;
// }
//enum type generated by protoc
type X int32
//enum constants generated by protoc
const (
X_A X = 0
X_B X = 1
X_C X = 2
X_D X = 3
)
func intToX(v int) (X, error) {
x := X(v)
switch x {
case X_A, X_B, X_C, X_D:
return x, nil
}
return 0, errors.New("could not convert int to X")
}
func main() {
for i := -1; i < 10; i++ {
if x, err := intToX(i); err != nil {
fmt.Println("unhandled error:", err, "for input value", i)
} else {
fmt.Printf("%d => X(%d)\n", i, x)
}
}
}
Question: Is there a better, more idiomatic way to map incoming integer values to protoc-generated constants?
In particular, I would like to avoid listing all constants explicitly in the case A, B, C, D statement.
I do not know which proto generation package you are using, but with github.com/golang/protobuf/proto you also get the reverse mapping of enums.
Example xyz.pb.go generated file:
type TimeInterval int32
const (
TimeInterval_TI_UNKNOWN TimeInterval = 0
TimeInterval_TI_HOUR TimeInterval = 1
TimeInterval_TI_DAY TimeInterval = 2
TimeInterval_TI_WEEK TimeInterval = 3
TimeInterval_TI_MONTH TimeInterval = 4
TimeInterval_TI_QUARTER TimeInterval = 5
TimeInterval_TI_YEAR TimeInterval = 6
)
var TimeInterval_name = map[int32]string{
0: "TI_UNKNOWN",
1: "TI_HOUR",
2: "TI_DAY",
3: "TI_WEEK",
4: "TI_MONTH",
5: "TI_QUARTER",
6: "TI_YEAR",
}
var TimeInterval_value = map[string]int32{
"TI_UNKNOWN": 0,
"TI_HOUR": 1,
"TI_DAY": 2,
"TI_WEEK": 3,
"TI_MONTH": 4,
"TI_QUARTER": 5,
"TI_YEAR": 6,
}
func (x TimeInterval) String() string {
return proto.EnumName(TimeInterval_name, int32(x))
}
func (TimeInterval) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
So with this you could test for existence in the following manner:
if _, found := TimeInterval_name[testinputint]; found{
//all ok
} else {
//not a value for this enum
}
Yes as #RickyA mentions using a range is nice as it verifies for all the possible underlying const values.
Additionally you could check the length of the enum, although that would only be possible when the underlying enum values do not have any 'gaps' and have a consequitive range of numbers.
Verbose explanation via code:
typelength := int32(len(TimeInterval_name))
if testinputint < 0 || int32(testinputint) >= typelength {
// not a value for this enum, return err
}
A bit less verbose, and just using int instead of int32
if testinputint < 0 || int(testinputint) >= len(TimeInterval_name) {
// not a value for this enum, return err
}
But as said, this will only be valid for enums that adhere to a proper iota. That might not be the case when you have changed your enum to read something like this:
var TimeInterval_name = map[int32]string{
0: "TI_UNKNOWN",
1: "TI_HOUR",
2: "TI_DAY",
3: "TI_WEEK",
// we do not use month anymore 4: "TI_MONTH",
5: "TI_QUARTER",
6: "TI_YEAR",
}
as the length of the generated map will be clearly less than six :)
In other words use the found method of #Ricky_A above to keep on the safe side.

Slicing using pointer to a slice

I am trying to modify slice a slice in another function, using the following code:
type DT struct {
name string
number int
}
func slicer(a *[]DT) {
tmp := *a
var b []DT
b = append(b, tmp[:1], tmp[2:])
*a = b
}
func main() {
o1 := DT {
name: "name-1",
number: 1,
}
o2 := DT {
name: "name-2",
number: 2,
}
o3 := DT {
name: "name-3",
number: 3,
}
b := make([]DT, 0)
b = append(b, o1)
b = append(b, o2)
b = append(b, o3)
slicer(&b)
fmt.Println(b)
}
What I want is, 1st and last element of the slice. But, in doing so, I am getting following error:
cannot use tmp[:1] (type []DT) as type DT in append
I am relatively new to Go Language, so kindly guide me through this one!
You should use operator ... to convert slice into list of variadic arguments.
b = append(b, tmp[:1]...)
b = append(b, tmp[2:]...)

setting values in a new declartion

Is it possible to include values in the declaration of a new type.
type Vertex struct {
X, Y int
}
func main() {
v := new( Vertex{ 0, 0} ) // Like so
fmt.Println( v )
// Instead of :
v = new(Vertex)
v.X, v.Y = 12, 4 // extra line for initializing the values of X and Y
fmt.Println( v )
}
Or because go makes the "Vertex{val, val} " a literal value instead of a basic Vertex type it's not possible?
You don't actually need "new", you can simply write:
v := Vertex{1,2}
If you want a struct with all members set to the zero value of their types (e.g., 0
for ints, nil for pointers, "" for strings, etc.), it's even simpler:
v := Vertex{} // same as Vertex{0,0}
You can also only initialize some of the members, leaving the others with their zero value:
v := Vertex{Y:1} // same as Vertex{0,1}
Note that with these v will be a variable of type Vertex. If you want a pointer to a Vertex, use:
v := &Vertex{1,2}

Go Unpacking Array As Arguments

So in Python and Ruby there is the splat operator (*) for unpacking an array as arguments. In Javascript there is the .apply() function. Is there a way of unpacking an array/slice as function arguments in Go? Any resources for this would be great as well!
Something along the lines of this:
func my_func(a, b int) (int) {
return a + b
}
func main() {
arr := []int{2,4}
sum := my_func(arr)
}
You can use a vararg syntax similar to C:
package main
import "fmt"
func my_func( args ...int) int {
sum := 0
for _,v := range args {
sum = sum + v
}
return sum;
}
func main() {
arr := []int{2,4}
sum := my_func(arr...)
fmt.Println("Sum is ", sum)
}
Now you can sum as many things as you'd like. Notice the important ... after when you call the my_func function.
Running example: http://ideone.com/8htWfx
Either your function is varargs, in which you can use a slice with the ... notation as Hunter McMillen shows, or your function has a fixed number of arguments and you can unpack them when writing your code.
If you really want to do this dynamically on a function of fixed number of arguments, you can use reflection:
package main
import "fmt"
import "reflect"
func my_func(a, b int) (int) {
return a + b
}
func main() {
arr := []int{2,4}
var args []reflect.Value
for _, x := range arr {
args = append(args, reflect.ValueOf(x))
}
fun := reflect.ValueOf(my_func)
result := fun.Call(args)
sum := result[0].Interface().(int)
fmt.Println("Sum is ", sum)
}
https://play.golang.org/p/2nN6kjHXIsd
I had a reason to unpack some vars from a map[string]string with single quotes around some of them as well as without. Here's the logic for it and the play link up top has the full working snippet.
func unpack(a map[string]string) string {
var stmt, val string
var x, y []string
for k, v := range a {
x = append(x, k)
y = append(y, "'"+v+"'")
}
stmt = "INSERT INTO tdo.rca_trans_status (" + strings.Join(x, ", ")
val = ") VALUES (" + strings.Join(y, ",") + ");"
return stmt + val}
Which presents cleanly for a mssql query as:
INSERT INTO tdo.rca_trans_status (rca_json_body, original_org, md5sum, updated, rca_key) VALUES ('blob','EG','2343453463','2009-11-10 23:00:00','prb-180');
No, there's no direct support for this in the language. Python and Ruby, as well as Javascript you're mentioning; are all dynamic/scripting languages. Go is way closer to, for example, C than to any dynamic language. The 'apply' functionality is handy for dynamic languages, but of little use for static languages like C or Go,

Resources