Strings passed to fmt.Sprintf are Enclosed in Square Brackets - go

Given the following code snippet
package main
import (
"fmt"
)
type Message string
const (
ConnectRepositoryError Message = "failed to connect %s repository"
)
func main() {
fmt.Println(ConnectRepositoryError.M("user"))
}
func (m Message) M(args ...string) string {
return fmt.Sprintf(string(m), args)
}
... why does fmt.Sprintf enclose the specified string (user) in square brackets?
failed to connect [user] repository
I'd expect this output instead:
failed to connect user repository

Because inside Message.M() you call fmt.Sprintf(), to which you pass the format string string(m) and a single argument args being a string slice. And slices are formatted like: the elements separated by space, enclosed in square brackets. And since you pass a single value to M(), it'll be a one-element slice, printed like [user].
Instead you want to pass args as the variadic parameter to fmt.Sprintf(), so use ....
Of course this will be a compile time-error because fmt.Sprintf() expects ...interface{} and ...string does not qualify. You have to change the parameter of M to be ...interface{}:
func (m Message) M(args ...interface{}) string {
return fmt.Sprintf(string(m), args...)
}
With this change output will be (try it on the Go Playground):
failed to connect user repository

The argument list for variable argument functions is a slice. When you print a slice, it is printed as [value value ...].
You should convert the strings to an []interface{}, and use the following syntax to pass all elements of the slice:
func (m Message) M(args ...string) string {
iargs:=make([]interface{},0,len(args))
for _,x:=range args {
iargs=append(iargs, x)
}
return fmt.Sprintf(string(m), iargs...)
}

Related

Go cannot use name (type []string) as type string for ...string argument [duplicate]

This question already has answers here:
How can I pass a slice as a variadic input?
(3 answers)
Closed 8 months ago.
i have this problem during compiling this code:
./greet.go:11:29: cannot use name (type []string) as type string in argument to CheckStringsAreUppercase
But i dont understand why. name ...string and words ...string have exactly same type. What's going on?
func Greet(name ...string) string {
helloWord := "Hello"
if CheckStringsAreUppercase(name) {
return strings.ToUpper(helloWord)
}
return helloWord
}
func CheckStringsAreUppercase(words ...string) bool {
for _, word := range words {
if !CheckStringIsUppercase(word) {
return true
}
}
return true
}
The ... notation (in a function signature) denotes a variadic parameter, which, inside that function scope is equivalent to a slice of that type, so ...string is equivalent to []string.
When you pass N parameters into a variadic function, all of them must be assignable to the type T of the vararg.
So by passing the parameter as CheckStringsAreUppercase(name) the type of name is effectively []string, but []string is not assignable to the type string of the variadic parameter ...string, hence your compiler error.
The correct way to "explode" a slice into a variadic parameter is to use the three dots ... after the variable name: CheckStringsAreUppercase(name...)
Use this syntax:
if CheckStringsAreUppercase(name...) {
return strings.ToUpper(helloWord)
}

String function in syscall.Errno

Reading through the 7.8 section of "The Go Programming Language" I spotted following code:
var err error = syscall.Errno(2)
fmt.Println(err.Error()) // "no such file or directory"
fmt.Println(err) // "no such file or directory"
I understand the first and second line. error interface is saitisfied by syscall.Errno, thus Error() function returning string is available.
I don't understand third one. Going through syscall's sources I can't find any place where syscall.Errno satisfies stringer interface - String() function is not defined.
Why third one prints string representation of sysscall.Errno?
The answer is found in the fmt documentation here:
If the format (which is implicitly %v for Println etc.) is valid for a string (%s %q %v %x %X), the following two rules apply:
If an operand implements the error interface, the Error method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
If an operand implements method String() string, that method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
So actually, for any value that supports both, the String() method is never called at all, since the error interface takes precidence over the Stringer interface. You can test this with a program like this one:
package main
import (
"fmt"
)
type foo string
func (f foo) String() string {
return "string"
}
func (f foo) Error() string {
return "error"
}
func main() {
fmt.Println(foo(""))
}
Output:
error

Go Function Types as Interface Values

I have been reading about function types as interface values in go and I came across an example that I have not been able to figure out. Here it is:
type binFunc func(int, int) int
func add(x, y int) int { return x + y }
func (f binFunc) Error() string { return "binFunc error" }
func main() {
var err error
err = binFunc(add)
fmt.Println(err)
}
You can find it on Go Playground here.
I understand that you can assign a method to a function type, but I do not understand how Error() is being called.
The docs for the fmt package have this to say:
Except when printed using the verbs %T and %p, special formatting
considerations apply for operands that implement certain interfaces.
In order of application:
...
If the format (which is implicitly %v for Println etc.) is valid for a
string (%s %q %v %x %X), the following two rules apply:
If an operand implements the error interface, the Error method will be invoked to convert the object to a string, which will then be
formatted as required by the verb (if any).
If an operand implements method String() string, that method will be invoked to convert the object to a string, which will then be
formatted as required by the verb (if any).
In other words, fmt.Println will attempt to print the string representation of the interface. Since the error interface is satisfied by binFunc, it invokes the Error method of binFunc.

Arguments as functions with variable number of arguments

How can I pass in Golang a function as an argument that can have potentially multiple arguments, e.g. fmt.Printf?
The first problem is that one has to define the type of the function to be passed first.
type FunctionWithVariableArgumentLength func(s string, object1 type1, ..., objectn typen)
The second problem is that one does not know what types the arguments in the list may have, like in fmt.Printf.
There's a prototype as for other functions : http://golang.org/pkg/fmt/#Printf
So you can define your function accepting a function as argument like this :
func exe(f func(string, ...interface{}) (int, error)) {
f("test %d", 23)
}
func main() {
exe(fmt.Printf)
}
Demonstration
You would use a similar signature as the one for fmt.Printf
func yourFunction(a ...interface{})
To answer the 1st part: writing functions with a variable number of arguments.
// sums returns the total of a variable number of arguments
func sum(numbers ...int) total int {
total = 0
for _, n := range numbers {
total += n
}
return total
}
The 2nd Part is harder but the function definition looks like:
func doVarArgs(fmt string, a ...interface{}) {
The variable a contains a slice of values of the type interface{}. You then iterate over the slice pulling each argument and using the package "reflect" to query the type of each argument.
See http://golang.org/pkg/reflect/ for a full explanation.

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:]...)
}

Resources