Golang pass nil as optional argument to a function? - go

In Golang, http.NewRequest has a specification like this:
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
However, I can pass nil as the body option if I don't want to pass the body to an io.Reader object, like this:
req, err := http.NewRequest("GET", "http://www.blahblah.org", nil)
How do I implement this functionality in my code? I have a function that I want to pass an optional string value so that it can page through API results however if I pass a nil to the string input I get this:
./snippets.go:32: cannot convert nil to type string
The parameters for my function look like this:
func getChallenges(after string) ([]challenge, string, error)

Go does not have "optional" arguments as a generally understood concept in other languages; nil is just the zero value for an interface (io.Reader in this case).
The equivalent zero value for a string is an empty string:
getChallenges("")
If you want to accept 0 or more of the same argument type, you use the variadic syntax:
func getChallenges(after ...string) ([]challenge, string, error)

You can modify you function to receive pointer value, like this:
func getChallenges(after *string) ([]challenge, string, error)
Then you can pass nil as an argument to it. But don't forget to check after for nil value inside your function before dereferencing it, or you will get a nil pointer exception:
func getChallenges(after *string) ([]challenge, string, error) {
if after == nil {
// No value specified
} else {
fmt.Printf("After: %s\n", *after) // Note pointer dereferencing with "*"
}
// ...
}
Another option:
Just use two functions:
func getChallenges(after string) {}
func getAllChallenges() {
return getChallenges(/* some default value here */)
}

you can use ellipse operator to send the optional parameters.. don't pass anything in optional parameter and check the length of parameter.
it should solve your problem
func foo(params ...int) {
fmt.Println(len(params))
}
func main() {
foo()
foo(1)
foo(1,2,3)
}

Maybe wrap it in a struct?
type NilableString struct {
value string;
}

You can use reflect.
In fact io.Reader is a interface.
So you can define signature like func getChallenges(after interface{}) ([]challenge, string, error)
interface{} is a empty interface, that is interface for anything.
But I suggest you use syntax args... to pass slice , refer to fmt.Printf implementation for usage, because if you pass no string the slice len is 0 and
this will avoid the reflect which I think too heavy for your function.

Related

golang: trying to use `any` to match different functions

Given this function:
func Apply(label string, func(msg any, arg any) (any, error) {
...
}
Unfortunately the funcs I need to pass to Apply don't match in their signature.
//Func 1
func GetAddress(prompt string, custom Param) (string, error) {}
// Func 2
func GetIdentifier(prompt string) (utils.ID, error) {}
But why does the compiler (go1.18) complain if I try
Apply(label, GetAddress)
with
[compiler IncompatibleAssign] [E] cannot use GetAddress (value of
type func(prompt string, custom Param) (string, error)) as
func(msg any, arg any) (any, error) value in argument to Apply
Why does msg any not match to prompt string, custom Param not to arg any and the string type in the return not to the any type in the return?
What exactly is incompatible here? (Ignore if you think the design is bad for a sec, I just want to understand that first).
Change the func declaration to use type parameters
func Apply[T1 any, T2 any, T3 any](label string, func(msg T1, arg T2) (T3, error)
Now to call it
Apply[string, Param, string](label, GetAddress)
When the function has type parameters, you may be able to drop the type parameter list with the compiler automatically inferring what the variant types are:
Apply(label, GetAddress)
The reason why the code you wrote doesn't actually work is that the types have to match exactly in the go language. The interface type is valid for incoming value types, the definition types must be the same in function, however, if the definition type is interface (or "any"), the parameter value can be any. (string|int ...)
func Apply(label string, func(msg any, arg any) (any, error) {
...
}
//Func 1
func GetAddress(prompt any, custom any) (any, error) {}
// Func 2
func GetIdentifier(prompt any) (any, error) {}
func Apply[K any](label string, func(msg K, arg K) (K, error) {
...
}
maybe something like this could work
Apply[K any]

How do you pass a slice of *interface{} as arguments?

I want to use Scan() in package sql, but the number of columns, and hence the number of arguments, will change at runtime. This is the signature of Scan():
func (rs *Rows) Scan(dest ...interface{}) error
According to the documentation, *interface{} is one of the types accepted by Scan(). So I want to create a slice of []*interface{} and that expand as arguments.
This is what I thought would work:
func query(database *sql.DB) {
rows, _ := database.Query("select * from testTable")
for rows.Next() {
data := make([]*interface{}, 2)
err := rows.Scan(data...) // Compilation error
fmt.Printf("%v%v\n", *data[0], *data[1])
if err != nil {
fmt.Println(err.Error())
}
}
}
Compilation fails with cannot use data (type []*interface {}) as type []interface {} in argument to rows.Scan. I thought that data... would expand to &data[0], &data[1], but apparently not. I don't understand the error message. *interface{} is compatible with interface{}, so why can't I expand the slice of pointers to interface types?
This works:
func query(database *sql.DB) {
rows, _ := database.Query("select * from testTable")
for rows.Next() {
data := make([]*interface{}, 2)
err := rows.Scan(&data[0], &data[1]) // Only changed this line
fmt.Printf("%v%v\n", *data[0], *data[1]) // Outputs "[48][116 101 120 116]"
if err != nil {
fmt.Println(err.Error())
}
}
}
I can't use this however, because the number of columns is unknown at compile time. How can I write this code so that I can pass a variable number of *interface{} to rows.Scan()?
First, you must not use []*interface{} slice of pointers to interface rather than []interface{} where the interfaces are pointers. []*interface{} is different from []interface{}. Just create a slice of interfaces where each element is a pointer to a concrete type.
Here is a snippet how you would do this.
var x int
var s string
data := []interface{}{&x, &s}
rows.Scan(data...)
Note on the use of the ... spread operator.
Here are some related questions that will explain a bit more:
golang: slice of struct != slice of interface it implements?
Cannot convert []string to []interface {}
If you really want to pass a []*interface{} (perhaps you don't know the concrete types of the output) you must first wrap each *interface{} in a interface{}:
values := make([]interface{}, columnsCount)
for i := range values {
values[i] = new(interface{})
}
Individual values passed into a ...interface{} parameter are automatically wrapped in a interface{}, but just like []int... won't satisfy ...interface{}, neither will []*interface{}....

interface{} to []string

I'm trying to parse some YAML file into go struct, but the file itself can not be treated as ordinary go structure: some of values may be either string or map[string][]string.
What I have tried is to do custom unmarshal func:
func (domain *Domain) UnmarshalYAML(unmarshal func(interface{}) error) error {
fmt.Println("Parsing domain")
var hostUrl interface{}
unmarshal(&hostUrl)
fmt.Println(reflect.TypeOf(hostUrl))
switch hostUrl.(type) {
case string:
domain.Host = hostUrl.(string)
case map[interface {}]interface {}:
fmt.Println("got map")
v := reflect.ValueOf(hostUrl)
fmt.Println(v.MapKeys()[0])
for _, host := range v.MapKeys() {
domain.Host = host.Interface().(string)
fmt.Println(v.MapIndex(host))
//HERE I NEED TO DO SMTH LIKE THIS:
//domain.Urls = v.MapIndex(host).([]string)
}
default:
return errors.New("invalid config file, cant parse domains")
}
return nil
}
My domain structure looks like this:
type Domain struct {
Host string
Urls []string
}
But this code causes an error:
invalid type assertion: v.MapIndex(host).([]string) (non-interface type reflect.Value on left)
So my question may sound like "how to convert {}interface to []string?" or it may become more complex: "How to parse YAML file into go struct if some key can be either simple string or map[string][]string?"
UPDATE:
Responding to #mkopriva:
fmt.Println(v.MapIndex(host))
for url := range v.MapIndex(host).Interface().([] interface{}) {
fmt.Println(url)
}
Didnt help me, as now it just prints some integers (0), but there should be a string. Converting it to an array of strings throws another error:
panic: interface conversion: interface {} is []interface {}, not []string
Thanks to #mkopriva and the snippet from the sandbox. The reason of integer appearing during iterating over v.MapIndex(host).Interface().([] interface{}) is that range returns two values: index and corresponding to that index value. I was only catching the first one. It is why I wasn't able to cast it to string.
Working loop:
for _, url := range v.MapIndex(host).Interface().([] interface{}) {
fmt.Println(url.(string))
domain.Urls = append(domain.Urls,url.(string) )
}

Golang pass nil value as an interface through reflection

I have a function with interface argument:
func f(e error) {
if e == nil {
fmt.Println("YEY! NIL") // how to get here?
} else {
fmt.Println("NOT NIL :(")
}
}
How do I pass it a nil value via reflect so that it passes == nil check?
Approach 1:
func main() {
rf := reflect.ValueOf(f)
nilArg := reflect.Zero(reflect.TypeOf((error)(nil))) // panic: reflect: Zero(nil)
rf.Call([]reflect.Value{nilArg})
}
Approach 2:
type MyError struct{}
func (e MyError) Error() string {
return ""
}
func main() {
rf := reflect.ValueOf(f)
nilArg := reflect.Zero(reflect.TypeOf(&MyError{})) // NOT NIL :(
rf.Call([]reflect.Value{nilArg})
}
Second approach doesn't work due to https://golang.org/doc/faq#nil_error
Playground: https://play.golang.org/p/V0bMSPcCKI
Use the expression reflect.TypeOf((*error)(nil)).Elem() to get the reflect.Type for interface error.
The first approach in the question does not work because the expression reflect.TypeOf((error)(nil)) returns nil. The concrete type of a nil interface value is nil.
The trick is to pass a non-interface value to reflect.TypeOf() and use reflect methods to get the desired reflect.Type from there. In this answer, I pass a *error to reflect.TypeOf() and call Elem on the result to get the reflect.Type for error.
Use the following to create nilArg:
nilArg := reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())
playground example
You have two options, basically e in the arguments to function f is (*MyError)(nil) not nil. So either assert the interface back to type MyError or use more reflection to check if it's nil.
e.(*MyError) == nil
or
reflect.ValueOf(e).IsNil()
Runnable Example: https://play.golang.org/p/2KWguSx618
useful links:
Check for nil and nil interface in Go
https://groups.google.com/forum/#!topic/golang-nuts/wnH302gBa4I/discussion

Pass []string to a function that expects a variadic parameter

In order to don't repeat my self over and over I wanted to create a function that handles running some commands.
func runCommand(name string, arg ...string) error {
cmd := exec.Command(name, arg)
if err := cmd.Run(); err != nil {
return err
} else {
return nil
}
}
Once I try to run this I get the following error:
cannot use arg (type []string) as type string in argument to exec.Command
I had a look into the implementation of the os.Command and it looks that the function signature is exact what I supply.
Internally a []string should be the same as variadic parameter but for the compiler it seems not.
Is there a way to pass the variadic parameter into the Command?
You expand the []string with another ...
cmd := exec.Command(name, arg...)
From the language spec on Passing arguments to ... parameters
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.

Resources