Golang unpack variadic arguments - go

How to unpack a variadic argument by ... in golang:
func run(a, b string, list ...int) {
}
func call(list ...int) {
run(x, y, list...) // use "list..." is not allowed
}
func main() {
a := []int{1, 2, 3}
call(a...)
}
Why this is illegal? As i have known, the list argument is a slice which can be unpacked by ....

unpack variadic arguments
For your example,
package main
import "fmt"
func run(a, b string, list ...int) {
fmt.Println(list)
for i, e := range list {
fmt.Println(i, e)
}
}
func call(list ...int) {
fmt.Println(list)
var x, y string
run(x, y, list...) // use "list..." is allowed
}
func main() {
a := []int{1, 2, 3}
fmt.Println(a)
call(a...)
}
Playground: https://play.golang.org/p/NI0L-nVVUxm
Output:
[1 2 3]
[1 2 3]
[1 2 3]
0 1
1 2
2 3

Variadic arguments can only be expanded if the slice you're passing to the variadic function is taking the place of the variadic argument. You cannot combine individual arguments and an expanded slice in a single variadic argument for this reason. When you use expansion, the function receives the "expanded" slice itself as its variadic parameter - it's semantic sugar that can be confusing, because these calls are not equivalent:
foo := []string{"alpha","beta"}
bar(foo...) // bar receives the foo slice as its variadic parameter
bar(foo[0], foo[1]) // bar receives a new slice with these elements copied to it
You can find more details in the spec, though the example they use makes some of the explanation unclear, because their example variadic function has multiple parameters. The section beginning "If the final argument is assignable" refers to the "final argument" not in a generic "final argument to a variadic function" sense, but in a specific "final argument to the function in the example" sense.

you didn't declare variables x and y. Create them or just use quotes.
https://play.golang.org/p/gZnMALrM4Uh
package main
import (
"fmt"
)
func run(a, b string, list ...int) {
fmt.Println(list)
}
func call(list ...int) {
run("x", "y", list...) // ok
}
func main() {
a := []int{1, 2, 3}
call(a...)
}

Related

What is the evaluation order of function arguments?

I'm using this version of Go:
$ go version
go version go1.18 windows/amd64
The results are different when struct A has only one field and B has two or above fields, and it just happens when parameter type is interface.
I'm not sure if that's a bug:
package main
import (
"fmt"
)
func main() {
a := A{}
m("A", a, SetAI(&a))
b := B{}
m("B", b, SetBI(&b))
}
type A struct {
I int
S string
}
type B struct {
I int
}
func SetAI(a *A) A {
a.I = 10
return *a
}
func SetBI(b *B) B {
b.I = 10
return *b
}
func m(name string, arg1, arg2 interface{}) {
fmt.Println(name+":", arg1, arg2)
}
I expected this output:
A: {10} {10}
B: {10} {10}
Instead I got this:
A: {0 } {10 }
B: {10} {10}
The source of confusion and different output is the order of evaluation of the arguments.
Look at your example:
m("A", a, SetAI(&a))
This is a function call, the function value and arguments are evaluated in the usual order:
Otherwise, when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.
For example, in the (function-local) assignment
y[f()], ok = g(h(), i()+x[j()], <-c), k()
the function calls and communication happen in the order f(), h(), i(), j(), <-c, g(), and k(). However, the order of those events compared to the evaluation and indexing of x and the evaluation of y is not specified.
So basically the spec only guarantees that function calls and communication
ops happen from left-to-right.
Your call has arguments "A", a and SetAI(&a). There is no guarantee if the second argument a is evaluated before the &a param passed to SetAI(), and this very much matters because SetAI() modifies a. Since the order is not guaranteed, you can't rely on which will be evaluated first, both order is valid by the spec.
If you make the evaluation explicit by doing a copy of the struct before, you get the same result:
a := A{}
aCopy := a
m("A", aCopy, SetAI(&a))
b := B{}
bCopy := b
m("B", bCopy, SetBI(&b))
This will output (try it on the Go Playground):
A: {0 } {10 }
B: {0} {10}
Or if you want the function call to be evaluated first:
a := A{}
ap := SetAI(&a)
m("A", a, ap)
b := B{}
bp := SetBI(&b)
m("B", b, bp)
This will output 10 for each cases (try this one on the Go Playground):
A: {10 } {10 }
B: {10} {10}

Truncate slice in place

Given a slice (not a pointer to a slice!) is there any way to truncate it in place?
The naive implementation doesn't work, because of scope:
package main
import (
"fmt"
)
func truncate(s []int, to int) []int{
s = s[:to] # <- has no effect outside this function
return s
}
func main() {
s := []int{0, 1, 2, 3, 4}
s1 := truncate(s, 3)
fmt.Printf("%v\n", s1)
fmt.Printf("%v\n", s)
}
prints
[0 1 2]
[0 1 2 3 4] # <- can we get [0 1 2] here?
Is there any way to modify the length or capacity of an existing slice, or are they immutable?
ETA: I thought this was obvious enough, but apparently not: when I ask whether it's possible to do this in place, I mean without reassigning s.
This is the way to go:
package main
import "fmt"
func main() {
s := []int{0, 1, 2, 3, 4}
s = truncate(s, 3)
fmt.Println(s) // [0 1 2]
}
func truncate(s []int, to int) []int {
return s[:to]
}
Slice is like a window to an underlying array.
The other way using pointer to the slice:
package main
import "fmt"
func main() {
s := []int{0, 1, 2, 3, 4}
truncate(&s, 3)
fmt.Println(s) // [0 1 2]
}
func truncate(s *[]int, to int) {
*s = (*s)[:to]
}
This can't be done, because slices are passed by value.
Note that as of April 2013, the Go language specification no longer refers to slices, maps, and channels as "reference types". (The behavior didn't change, just the language used to describe it.)
It can't be done with arrays either, because an array's length is part of its type.

os.Read() How is work? Golang

Why if I print bs, before calling Read(), it prints nothing, but after the call file.Read(bs), it shows the inside of test.txt file. Unless bs is only argument, how Read() can Change it?
package main
import (
"os"
"fmt"
)
func main() {
file , err := os.Open("test.txt")
if err == nil {
} else {
}
stat , _ := file.Stat()
bs := make([]byte, stat.Size())
fmt.Println(string(bs))
bsf ,err := file.Read(bs)
if err != nil{
fmt.Println(err)
fmt.Println(bsf)
}
fmt.Println(string(bs))
}
Output:
(Line1)
(Line2)hi, This is Example text in test.txt file.
Unless bs is only argument, how Read() can Change it?
It seems that you may be missing basic knowledge about programming languages in general. There are different kind of "values". There are pointers (or references) and there are the "usual values".
For example:
package main
import (
"fmt"
)
func changeIt(p *int) {
*p = 9
}
func main() {
a := 1
fmt.Println(a)
changeIt(&a)
fmt.Println(a)
}
It'll print 1 9 not 1 1. *int is not an integer, but a pointer to an integer. A pointer is a value that points (references) another value. If you have a value of type pointer you get the actual value that the pointer points to by using * (which is called dereferencing):
func main() {
a := 1
b := &a
fmt.Println(b, *b)
}
b is a pointer (of type *int) that points to a. The println will print the location of a followed by the value of a which is usually something like uhm 0x10414020 1. We can also modify the value a pointer points to by using *p = ...:
func main() {
a := 1
b := &a
*b = 9
fmt.Println(b, *b, a)
}
which will print 0x10414020 9 9.
Now, []byte is a slice... slices are like pointers. When you do
func changeIt(buf []byte) {
buf[0] = 10
}
func main() {
data := []byte{1,2,3}
changeIt(data)
fmt.Println(data)
}
You're not actually passing the values [1 2 3] to changeIt but a pointer to those values. Thus here the println will show [10 2 3]. Compare this to:
func changeIt(buf [3]byte) {
buf[0] = 10
}
func main() {
data := [3]byte{1,2,3}
changeIt(data)
fmt.Println(data)
}
Which will print [1 2 3] and it will pass the values [1 2 3] and not a pointer so changeIt essentially works on a copy and the buf[0] = 10 has no effect. Remember: [n]T is an array, []T is a slice. [n]T is a "raw value" and []T is a "pointer value".

swap function not working in golang

Actually i just start to learn golang . In the beginning i think that = and := are same . But then i understand that there is some difference between this two .
I learned swap function in golnag
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
But when i rewrite this function using var this is not working
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
var a, b string
a ="hello"
b="world"
swap(a, b)
fmt.Println(a, b)
}
what is the error in this program ?
Another solution is to use pointers:
package main
import "fmt"
func swap(x, y *string) {
*x, *y = *y, *x
}
func main() {
var a, b string
a ="hello"
b="world"
swap(&a, &b)
fmt.Println(a, b)
}
https://play.golang.org/p/-vxUMlaVmN
The reason is that in second case values returned from swap are ignored. SO nothing is changed.
Try: https://play.golang.org/p/uADEf5X15g
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
var a, b string
a = "hello"
b = "world"
a, b = swap(a, b) //// <----
fmt.Println(a, b)
}
To respond your initial question, you should assign the values returned by swap to a and b like so
a, b = swap(b, a)
Notice that this is simple assignment , without the : attached to the equal
also, instead of a swap function, you could just try inplace reassignment:
a, b = b, a
Variables declaration
var a string - declaration of a variable with null value
a := "spam" - declaration of a variable with a concrete value
func f(a, b string) (string, string) { - declaration of a function with value parameters. It means you have new variables with passed values as arguments each time you call a function.
func f(a, b *string) (*string, *string) { - declaration of a function with pointer arguments. In it's turn it means you have pointers to passed variables each time you call the function.
Also...
a := *string - declaration of a pointer variable.
*a - value of a pointer variable.
&a - pointer of a value
In-place swap
To swap in-place (without returning and reassigning) you should swap values between pointers.
func swap(a, b *string) {
*a, *b = *b, *a
}
p.s.
Take into account that strings is read-only slices of bytes. And slices are reference type it means that an array behind the sub-slices of a common array or slice is the same. It doesn't related to the question but should be considered in such cases.

Inserting Missing value NOT working GoLang

I'm trying to Insert a Int value to slice if it is missing in that.
My Code :
package main
import (
"fmt"
)
func AppendIfMissing(slice []int, i int) []int {
for _, ele := range slice {
if ele == i {
fmt.Println(i)
return slice
}
}
fmt.Println("i value is ", i)
slice = append(slice, i)
return slice
}
func main() {
slice1 := []int{1, 2, 3, 4}
AppendIfMissing(slice1, 60)
fmt.Println("slice after adding :", slice1)
}
OutPut :
i value is 60
slice after adding : [1 2 3 4]
Appending to slice is not happening.What is wrong with my code ?
AppendIfMissing returns a slice which you need to affect to a variable.
append(slice, i) creates a new slice, which means the parameter slice isn't modified, it refers to a all new slice:
which is returned at the end
which needs to be affected to a variable
slice1 = AppendIfMissing(slice1, 60)
See go playground example.
I agree that the article "Arrays, slices (and strings): The mechanics of 'append'" mentions
Even though the slice header is passed by value, the header includes a pointer to elements of an array, so both the original slice header and the copy of the header passed to the function describe the same array.
Therefore, when the function returns, the modified elements can be seen through the original slice variable.
But the function in that article wasn't using append:
func AddOneToEachElement(slice []byte) {
for i := range slice {
slice[i]++
}
}
the contents of a slice argument can be modified by a function, but its header cannot
And by doing
slice = append(slice, i)
you modify the header, you reallocate the resulting slice to a completely different array.
That won't be visible outside of the function.
More generic version of AppendIfMissing, it requires go version >= 1.18
func AppendIfMissing[T comparable](slice []T, i T) []T {
for _, ele := range slice {
if ele == i {
return slice
}
}
return append(slice, i)
}

Resources