20

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)
Nik
  • 2,885
  • 2
  • 25
  • 25

6 Answers6

14

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)
JimB
  • 104,193
  • 13
  • 262
  • 255
  • The empty string "" was what I was using, I'm going to implement variadic syntax though. I assume this is similar to *arg/**kwargs from python. Cheers! –  Sep 14 '15 at 19:34
11

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 */)
}
coquin
  • 1,338
  • 1
  • 13
  • 22
  • 4
    A huge downside of making the argument a *string is the fact that you can't take the address of a string literal, nor does it automatically box up the string for you. https://play.golang.org/p/d4jvq5xrAk – Chris Harrington Sep 18 '16 at 07:26
  • 1
    The _"just add another function"_ argument can result in an explosion of methods required to deal with multiple permutations, requires the burden of deciding on meaningful, discoverable and rememberable names for those methods, and adds burden to learning the packages that use more methods vs. less. #jmtcw – MikeSchinkel Mar 20 '19 at 14:07
4

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)
}
Nitin Tripathi
  • 1,224
  • 7
  • 17
0

Maybe wrap it in a struct?

type NilableString struct {
    value string;
}
0

Here is how such functions accept nil as well as value.

req, err := http.NewRequest(method, path, reader)

reader should accept io.Reader or nil

packege io

type Reader struct {
  .....
}

func (r Reader) getData() []byte {
  ......
}

type ReaderInterface interface {
  getDate() []byte
}
package http

import "io"

func NewRequest(method string, path string, reader ReaderInterface) {
  if (reader != nil) {
    data := reader.getData()
  }
  .....
}

Note that NewRequest expecting type ReaderInterface but not Reader itself. Any type with GetData function like above or nil will be accepted. Inside you can check if reader is not nil.

dileep nandanam
  • 2,827
  • 18
  • 20
-1

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.

ggaaooppeenngg
  • 1,323
  • 3
  • 17
  • 27