Golang : Variable argument - go

When I compile the following program
func myPrint(v ...interface{}) {
fmt.Println("Hello", v...)
}
func main() {
myPrint("new", "world")
}
I get a compilation error
too many arguments in call to fmt.Println
I thought v... is going to expand into 2nd, 3rd arguments and the fmt.Println would see three item variadic argument list. I thought it would be equivalent to
fmt.Println("Hello", "new", "world")
Why is it giving an error.

Try this. It prepends Hello to the variadic arguments, then prints them all at once with println.
package main
import "fmt"
func myPrint(v ...interface{}) {
a := append([]interface{}{"Hello"}, v...) // prepend "Hello" to variadics
fmt.Println(a...) // println the whole lot
}
func main() {
myPrint("new", "world")
}

You're mis-using the variadic shorthand in your call to fmt.Println(). What you're actually sending is 2 arguments: a single string, then the slice of type interface{} expanded. The function call will not concatenate that into a single slice.
This design will compile and run with the results you're expecting:
func myPrint(v ...interface{}) {
fmt.Print("Hello ")
fmt.Println(v...)
}
func main() {
myPrint("new", "world")
}

Related

Go Flag Usage Description Contains the Word value

I've defined a custom flag for accepting a slice of strings as such:
type strSliceFlag []string
func (i *strSliceFlag) String() string {
return fmt.Sprint(*i)
}
func (i *strSliceFlag) Set(value string) error {
*i = append(*i, value)
return nil
}
I then parse it with
...
var tags strSliceFlag
flag.Var(&tags, "t", tFlagExpl)
flag.Parse()
...
When I build this program, and run it with the help flag: main -h, it prints out:
Usage of main:
-t value
Test explanation
My question is, where is the word value coming from? I can't find out how to remove it. I think it maybe has something to do with the default value for the flag.
value is the default argument name chosen by flag.UnquoteUsage for custom types (rendered via flag.(*FlagSet).PrintDefaults).
You can override the default with backquotes in your usage text. The backquotes are stripped from usage text. Eg:
package main
import (
"flag"
"fmt"
)
type stringSlice []string
func (s *stringSlice) String() string {
return fmt.Sprint(*s)
}
func (s *stringSlice) Set(v string) error {
*s = append(*s, v)
return nil
}
func main() {
var s stringSlice
flag.Var(&s, "foo", "append a foo to the list")
flag.Var(&s, "foo2", "append a `foo` to the list")
flag.Parse()
}
Running with -h shows how the argument name changes:
Usage of ./flagusage:
-foo value
append a foo to the list
-foo2 foo
append a foo to the list

Overriding printf method

I want to create a function that will work exactly like fmt.Printf but also left pad the string with current timestamp. Ideally I would like to override printf and println to do this job, but the first solution is also ok.
This is what I've done:
func output(message string, a ...interface{}) {
fmt.Printf(getCurrentTime() + " " + message, a)
}
func getCurrentTime() string {
t := time.Now()
return t.Format("[2006-01-02 15:04:05]")
}
But it outputs strange results when I pass variables.
How should I do this?
If you want to pass variadic elements from a function to another, you have to expand them. From your example, a is an []interface, so you are passing only two arguments to the actual fmt.Printf: the message and an array.
You have to correct the call:
fmt.Printf(getCurrentTime() + " " + message, a...)
A little example to show what happens:
func exec(args ...interface{}) {
fmt.Println(args)
}
func insert(args ...interface{}) {
exec(args) // Prints [[5 42]]
exec(args...) // Prints [5 42]
}
func main() {
insert(5, "42")
}

Golang: fmt, variadic args and %!(EXTRA type=value) error

I'm implementing a wrapper around the standard log package to make a logger with various log levels.
I have the following interface:
type Logger interface {
Trace(fmt string, args ...interface{})
Debug(fmt string, args ...interface{})
Info(fmt string, args ...interface{})
Warn(fmt string, args ...interface{})
Error(fmt string, args ...interface{})
Fatal(fmt string, args ...interface{})
Panic(fmt string, args ...interface{})
}
In the implementation I have something like this (not the exact code)
func Info(format string, args ...interface{}){
msg := fmt.Sprintf(format, args...)
log.Println(msg)
}
Now, assume I call my library like this:
logger.Info("Hello %s", "World")
I get the printout: "Hello %!(EXTRA string=WORLD)", instead of the expected "Hello World". There a similar output if I do
msg := fmt.Sprintf(format, args)
This returns "Hello World%!EXTRA []interface{}=[]".
I can't reproduce this behavior. Are you sure it's not a simple error that you forgot to show here?
https://play.golang.org/p/-jtmll17Xj
package main
import "fmt"
func Info(format string, args ...interface{}){
msg := fmt.Sprintf(format, args...)
fmt.Print(msg)
}
func main() {
Info("Hello %s", "World")
}
Prints
Hello World
According to the fmt docs, %!(EXTRA string=WORLD) is added to the string when you pass extra parameters, unexpected by the format. Maybe you are using the format string "Hello World" instead of "Hello %s", or passing the argument twice?
The error was between the chair and keyboard. I mixed up the following interfaces:
func Print(v ...interface{})
func Printf(format string, v ...interface{})
Some of my code was calling the library without a format string.See here for a more detailed example: http://play.golang.org/p/Xx79qujaFp
Happens aswell when your message has no verbs and the varargs are empty but coming from elsewhere:
func CustomPrintf(message string, a ...interface{}) {
fmt.Printf(message, a) // for no verbs in message you'll get this "EXTRA" suffix
}
That's a special case of a general error. You've got to spread a first. So use fmt.Printf(message, a...) to make it to varags again.
This can happen if you pass nil params to your format, for instance:
myStr := fmt.Sprintf("check/%s", "hello", nil)
will set the value of myStr to: "check/hello%!"
In this example the extra params was the third param with nil value.
so make sure to remove any extra params.
Also, happens due to a simple (typo?) if you forget/miss putting in the "%".
e.g.
fmt.Printf("v\n", myvar)
instead of
fmt.Printf("%v\n", myvar)
obviously not a "syntax" error, so nothing for the compiler to catch.
I realize this nets out to the same as some of the other answers, but I think this is clearer and (maybe?) the most common cause.

How can I pass a slice as a variadic input?

I have a function func more(... t). I'm wondering if it's possible to use a slice to populate a list of arguments ... .
I'm trying to solve the following program. Basically to mimic a normal shell which receives the command as a string.
Command function requires a "list" of arguments and I don't see how I can convert a string into a such list
import "os/exec"
import "strings"
func main(){
plainCommand := "echo hello world"
sliceA := strings.Fields(plainCommand)
cmd := exec.Command(sliceA)
}
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.
Package exec
func Command
func Command(name string, arg ...string) *Cmd
Command returns the Cmd struct to execute the named program with the
given arguments.
The returned Cmd's Args field is constructed from the command name
followed by the elements of arg, so arg should not include the command
name itself. For example, Command("echo", "hello")
For example,
package main
import (
"fmt"
"os/exec"
)
func main() {
name := "echo"
args := []string{"hello", "world"}
cmd := exec.Command(name, args...)
out, err := cmd.Output()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(out))
}
Output:
hello world
A list of command arguments can be retrieved from the flag package Args() function. You can then pass this to a function using the variadic input style (func(input...))
From the Spec:
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.
Example:
package main
import "fmt"
func echo(strings ...string) {
for _, s := range strings {
fmt.Println(s)
}
}
func main() {
strings := []string{"a", "b", "c"}
echo(strings...) // Treat input to function as variadic
}
See The Go spec for more details.
Playground
func Command
func Command(name string, arg ...string) *Cmd
Command returns the Cmd struct to execute the named program with the given arguments.
So you have to extract the command which is found at sliceA[0] and then pass all the arguments with a variadic but removing the command sliceA[1:]....
import "os/exec"
import "strings"
func main(){
plainCommand := "echo hello world"
sliceA := strings.Fields(plainCommand)
cmd := exec.Command(sliceA[0], sliceA[1:]...)
}

Map of methods in Go

I have several methods that I'm calling for some cases (like Add, Delete, etc..). However over time the number of cases is increasing and my switch-case is getting longer. So I thought I'd create a map of methods, like Go map of functions; here the mapping of functions is trivial. However, is it possible to create a map of methods in Go?
When we have a method:
func (f *Foo) Add(a string, b int) { }
The syntax below create compile-time error:
actions := map[string]func(a, b){
"add": f.Add(a,b),
}
Is it possible to create a map of methods in Go?
Yes. Currently:
actions := map[string]func(a string, b int){
"add": func(a string, b int) { f.Add(a, b) },
}
Later: see the go11func document guelfi mentioned.
There is currently no way to store both receiver and method in a single value (unless you store it in a struct). This is currently worked on and it may change with Go 1.1 (see http://golang.org/s/go11func).
You may, however, assign a method to a function value (without a receiver) and pass the receiver to the value later:
package main
import "fmt"
type Foo struct {
n int
}
func (f *Foo) Bar(m int) int {
return f.n + m
}
func main() {
foo := &Foo{2}
f := (*Foo).Bar
fmt.Printf("%T\n", f)
fmt.Println(f(foo, 42))
}
This value can be stored in a map like anything else.
I met with a similar question.
How can this be done today, 9 years later:
the thing is that the receiver must be passed to the method map as the first argument. Which is pretty unusual.
package main
import (
"fmt"
"log"
)
type mType struct {
str string
}
func (m *mType) getStr(s string) {
fmt.Println(s)
fmt.Println(m.str)
}
var (
testmap = make(map[string]func(m *mType, s string))
)
func main() {
test := &mType{
str: "Internal string",
}
testmap["GetSTR"] = (*mType).getStr
method, ok := testmap["GetSTR"]
if !ok {
log.Fatal("something goes wrong")
}
method(test, "External string")
}
https://go.dev/play/p/yy3aR_kMzHP
You can do this using Method Expressions:
https://golang.org/ref/spec#Method_expressions
However, this makes the function take the receiver as a first argument:
actions := map[string]func(Foo, string, int){
"add": Foo.Add
}
Similarly, you can get a function with the signature func(*Foo, string, int) using (*Foo).Add
If you want to use pointer to type Foo as receiver, like in:
func (f *Foo) Add(a string, b int) { }
then you can map string to function of (*Foo, string, int), like this:
var operations = map[string]func(*Foo, string, int){
"add": (*Foo).Add,
"delete": (*Foo).Delete,
}
Then you would use it as:
var foo Foo = ...
var op string = GetOp() // "add", "delete", ...
operations[op](&foo, a, b)
where GetOp() returns an operation as string, for example from a user input.
a and b are your string and int arguments to methods.
This assumes that all methods have the same signatures. They can also have return value(s), again of the same type(s).
It is also possible to do this with Foo as receiver instead of *Foo. In that case we don't have to de-reference it in the map, and we pass foo instead of &foo.

Resources