In Go, it's possible to prefix the final parameter in a function with the ... notation to indicate it is a variadic parameter.
template.ParseFiles is one such function:
func (t *Template) ParseFiles(filenames ...string) (*Template, error)
I am trying to create a function of my own which sets up various common features of my templates and I'd like the calling function to pass in the list of files that need to be parsed but I'm not sure how.
For example if my code looked like this:
type templateMap map[string]*template.Template
func (tmpl templateMap) AddTemplate(name string, files ...string) {
tmpl[name] = template.Must(template.ParseFiles(files)).Delims("{#","#}")
}
I get an error:
cannot use files (type []string) as type string in function argument
How do I wrapper variadic parameters?
To pass a slice in place of the variadic argument of a function, simply suffix it with .... So in your example code, you would instead want:
tmpl[name] = template.Must(template.ParseFiles(files...)).Delims("{#","#}")
Here is a simple example of the concept: http://play.golang.org/p/TpYNxnAM_5
Related
I have a function that's called quite a few times. How do I now add an additional parameter to that function without having to modify all the call-sites (essentially intruding the default value there and adding a bunch of noise) as well as keeping type safety?
All the languages I have previously used either support default arguments or overloading, so I am quite lost as to how I would do that.
Go doesn't have default arguments, neither it has function overloading. I think, the best you can do without changing the rest of the code is:
Rename the function Func() to FuncWithNewArg()
Add a new argument to FuncWithNewArg()
Create a new function named Func() with the original signature. Func() will call FuncWithNewArg() passing all its argument plus the default value for the new one.
The only way to add an optional argument to a function in Go is with a variadic function. As long as your function doesn't already have any variadic variables, you can add one without requiring all the existing callers to update. However, this does change the function signature, so if you have anything depending on that signature (i.e. assigning the function to a variable), such things may break.
To illustrate, suppose your function is:
func Foo(count int) error {
// do stuff
}
You could add an optional variadic variable at the end:
func Foo(count int, optional ...string) error {
// do stuff
}
You then access the optional variable as as a slice of the designated type ([]string in this case).
Now Foo() can be called as either Foo(3) or Foo(3, "bar").
Actually, it can be called with any number of arguments, so long as they match the type of the variadic variable. I.e. Foo(3, "bar", "baz", "qux") is also valid.
A function can take only a single variadic variable, and it must be the last one. This means you can't mix and match types. For example, this is invalid:
func Foo(count int, optional ...string, alsoOptional ...float64) error
If you need something more flexible than this, your best bet is to add a new function, as suggested in #bereal's answer:
func Foo(count int) error { ... }
func FooWithOther(count int, other string) error { ... }
func FooWithMany(count, int, other string, more bool) error { ... }
just see the code:(so simple that I can't believe myself)
package log
import "fmt"
func P(format string,a ...interface{}){
fmt.Printf(format,a)
}
when called somewhere like this :
log.P("%s,%s,%d","","",0)
I got error:
[ %!s(int=0)],%!s(MISSING),%!d(MISSING)
BUT if I call fmt.Printf directly like this:
fmt.Printf("%s,%s,%d","","",0)
It works perfectly,just perfectly (of course,as basic use of fmt).
So the Question is:
Why log.P not work??
FYI:
I believe it's pretty simple ,but I just can't find an answer by google,
never ever someone dropped in the hell ?
Or maybe I just dont know how to ask,so I put pure code above.
Or just I'm a super fool this time?
I signed up stackoverflow today for an answer to this. Let me know what's wrong with me. As soon...
It is just a little mistake. You are calling fmt.Printf with a as a single argument, while it is not. You need to pass it as a variadic argument.
package main
import (
"fmt"
)
func P(format string, a ...interface{}) {
fmt.Printf(format, a)
}
func P2(format string, a ...interface{}) {
fmt.Printf(format, a...)
}
func main() {
P("%s,%s,%d", "", "", 0)
fmt.Println()
P2("%s,%s,%d", "hello", "world", 0)
}
You can read about variadic parameters here.
You need to pass a to Printf as variadic and to convert an array to variadic, you need to follow this notation:
func P(format string, a ...interface{}){
fmt.Printf(format, a...)
}
The Go Programming Language Specification
Passing arguments to ... parameters
If f is variadic with final parameter type ...T, then within the
function the argument is equivalent to a parameter of type []T. At
each call of f, the argument passed to the final parameter is a new
slice of type []T whose successive elements are the actual arguments,
which all must be assignable to the type T. The length of the slice is
therefore the number of arguments bound to the final parameter and may
differ for each call site.
I am curious about unpacking a slice of slices and sending them as arguments to a variadic function.
Let's say we have a function with variadic parameters:
func unpack(args ...interface{})
If we wan't to pass in a slice of interfaces it works, it doesn't matter if we unpack it or not:
slice := []interface{}{1,2,3}
unpack(slice) // works
unpack(slice...) // works
It gets tricky if we have a slice of slices. Here the compiler doesn't let us pass in an unpacked version:
sliceOfSlices := [][]interface{}{
[]interface{}{1,2},
[]interface{}{101,102},
}
unpack(sliceOfSlices) // works
unpack(sliceOfSlices...) // compiler error
The error says:
cannot use sliceOfSlices (type [][]interface {}) as type []interface {} in argument to unpack
I don't know why this happens, as we can clearly pass an []interface{} type into the function. How can I call the method unpack with the unpacked content of sliceOfSlices as arguments?
Playground example: https://play.golang.org/p/O3AYba8h4i
This is covered in Spec: Passing arguments to ... parameters:
If f is variadic with a final parameter p of type ...T, then within f the type of p is equivalent to type []T.
...
If the final argument is assignable to a slice type []T, it may be passed unchanged as the value for a ...T parameter if the argument is followed by .... In this case no new slice is created.
So in short: it is a compile-time error because sliceOfSlices (which is of type [][]interface{}) cannot be assigned to args (which is of type []interface{}) (proof on Playground).
In long:
In your first example when you do unpack(slice), since unpack() expects values of interface{}, therefore slice (which is of type []interface{}) will be wrapped in a new interface{} value, and it will be passed as a single argument.
When you do unpack(slice...), this will pass all the values of slice as separate values to unpack(); this is possible because type of slice is []interface{}, it matches the type of the variadic parameter (args ...interface{}).
In your second example when you do unpack(sliceOfSlices), again, sliceOfSlices will be wrapped in a new interface{} value and passed as a single argument.
But when you try unpack(sliceOfSlices...), that would want to pass each element of sliceOfSlices to unpack(), but type of sliceOfSlices (which is [][]interface{}) does not match the type of the variadic parameter, hence the compile-time error.
The only way to pass sliceOfSlices to unpack() "exploded" is to create a new slice whose type must be []interface{}, copy the elements, then you can pass it using ....
Example:
var sliceOfSlices2 []interface{}
for _, v := range sliceOfSlices {
sliceOfSlices2 = append(sliceOfSlices2, v)
}
unpack(sliceOfSlices2...)
Try it on the Go Playground.
Let's use the following unpack() function to verify the number of arguments:
func unpack(args ...interface{}) {
fmt.Println(len(args))
}
Running your example (and with my new slice creation), output is:
1
3
1
2
Which proves without ... only a single argument is passed (wrapped in interface{}), and using ... all elements will be passed separately.
Try this test on the Go Playground.
I have a Printer interface that uses the standard go Printf function signature:
type Printer interface {
Printf(format string, tokens ...interface{})
}
I would like to be able to mock this interface using gomock, but I'm not sure how setup the tokens ...interface{} argument properly.
I expected that Printf(gomock.Any(), gomock.Any()) would cover all potential cases (since tokens compiles to []interface{}), but it appears you need to setup an explicit call for N number of tokens:
// no tokens
mockPrinter.EXPECT().
Printf(gomock.Any()).
AnyTimes()
// 1 token
mockPrinter.EXPECT().
Printf(gomock.Any(), gomock.Any()).
AnyTimes()
// 2 tokens
mockPrinter.EXPECT().
Printf(gomock.Any(), gomock.Any(), gomock.Any()).
AnyTimes()
// ... up to N tokens
Does anyone know of a better way to do this?
Not possible with the current version of gomock. Maybe you can extend it, and send a pull request in. To understand why it's not possible, you have to look at the mock generated for variadic functions.
To do that, let's look at the examples in gomock's repository, specifically ./sample/mock_user/user.go and ./sample/mock_user/mock_user.go.
Generated Mock
You'll see a function in the Index inteface called Ellip, which is like your Printf function:
type Index interface {
// ...
Ellip(fmt string, args ...interface{})
// ...
}
Now, here's what the mocked function looks like for Ellip:
func (_m *MockIndex) Ellip(_param0 string, _param1 ...interface{}) {
_s := []interface{}{_param0}
for _, _x := range _param1 {
_s = append(_s, _x)
}
_m.ctrl.Call(_m, "Ellip", _s...)
}
Notice anything odd? Well, gomock is creating a slice of interfaces, _s, initialized with the first parameter. Then it appends the variadic parameters to that slice of interfaces, _s.
So, to be clear, it doesn't just append the variadic parameter, _param1, to the slice. Each individual variadic from _param1 is appended to the new slice, by iterating through it.
This means that the slice of variadic parameters is not preserved. It's broken out.
As of October 1, 2017, gomock.Any() works correctly for variadic args: https://github.com/golang/mock/pull/101
Below is a piece of Go code I have question about.
Specifically, what is a in this function?
func DPrintf(format string, a ...interface{}) (n int, err error) {
if Debug > 0 {
n, err = fmt.Printf(format, a...)
}
return
}
Could anyone tell me what the three dots are here?
And what does ...interface{} do?
A parameter type prefixed with three dots (...) is called a variadic parameter. That means you can pass any number or arguments into that parameter (just like with fmt.Printf()). The function will receive the list of arguments for the parameter as a slice of the type declared for the parameter ([]interface{} in your case). The Go Specification states:
The final parameter in a function signature may have a type prefixed with .... A function with such a parameter is called variadic and may be invoked with zero or more arguments for that parameter.
A parameter:
a ...interface{}
Is, for the function equivalent to:
a []interface{}
The difference is how you pass the arguments to such a function. It is done either by giving each element of the slice separately, or as a single slice, in which case you will have to suffix the slice-value with the three dots. The following examples will result in the same call:
fmt.Println("First", "Second", "Third")
Will do the same as:
s := []interface{}{"First", "Second", "Third"}
fmt.Println(s...)
This is explained quite well in the Go Specification as well:
Given the function and calls
func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
within Greeting, who will have the value nil in the first call, and []string{"Joe", "Anna", "Eileen"} in the second.
If the final argument is assignable to a slice type []T, it may be passed unchanged as the value for a ...T parameter if the argument is followed by .... In this case no new slice is created.
Given the slice s and call
s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)
within Greeting, who will have the same value as s with the same underlying array.
As far as the interface{} term, it is the empty interface. In other words, the interface implemented by all variables in Go.
This is sort of analogous to java.lang.Object or System.Object in C#, but is instead inclusive of every variable type in the language. So it lets you pass in anything to the method.