One place for hosting & domains

      Functions

      How To Use Variadic Functions in Go


      Introduction

      A variadic function is a function that accepts zero, one, or more values as a single argument. While variadic functions are not the common case, they can be used to make your code cleaner and more readable.

      Variadic functions are more common than they seem. The most common one is the Println function from the fmt package.

      func Println(a ...interface{}) (n int, err error)
      

      A function with a parameter that is preceded with a set of ellipses (...) is considered a variadic function. The ellipsis means that the parameter provided can be zero, one, or more values. For the fmt.Println package, it is stating that the parameter a is variadic.

      Let’s create a program that uses the fmt.Println function and pass in zero, one, or more values:

      print.go

      package main
      
      import "fmt"
      
      func main() {
          fmt.Println()
          fmt.Println("one")
          fmt.Println("one", "two")
          fmt.Println("one", "two", "three")
      }
      

      The first time we call fmt.Println, we don’t pass any arguments. The second time we call fmt.Println we pass in only a single argument, with the value of one. Then we pass one and two, and finally one, two, and three.

      Let’s run the program with the following command:

      We’ll see the following output:

      Output

      one one two one two three

      The first line of the output is blank. This is because we didn’t pass any arguments the first time fmt.Println was called. The second time the value of one was printed. Then one and two, and finally one, two, and three.

      Now that we have seen how to call a variadic function, let’s look at how we can define our own variadic function.

      Defining a Variadic Function

      We can define a variadic function by using an ellipsis (...) in front of the argument. Let’s create a program that greets people when their names are sent to the function:

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          sayHello()
          sayHello("Sammy")
          sayHello("Sammy", "Jessica", "Drew", "Jamie")
      }
      
      func sayHello(names ...string) {
          for _, n := range names {
              fmt.Printf("Hello %sn", n)
          }
      }
      

      We created a sayHello function that takes only a single parameter called names. The parameter is variadic, as we put an ellipsis (...) before the data type: ...string. This tells Go that the function can accept zero, one, or many arguments.

      The sayHello function receives the names parameter as a slice. Since the data type is a string, the names parameter can be treated just like a slice of strings ([]string) inside the function body. We can create a loop with the range operator and iterate through the slice of strings.

      If we run the program, we will get the following output:

      Output

      Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

      Notice that nothing printed for the first time we called sayHello. This is because the variadic parameter was an empty slice of string. Since we are looping through the slice, there is nothing to iterate through, and fmt.Printf is never called.

      Let’s modify the program to detect that no values were sent in:

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          sayHello()
          sayHello("Sammy")
          sayHello("Sammy", "Jessica", "Drew", "Jamie")
      }
      
      func sayHello(names ...string) {
          if len(names) == 0 {
              fmt.Println("nobody to greet")
              return
          }
          for _, n := range names {
              fmt.Printf("Hello %sn", n)
          }
      }
      

      Now, by using an if statement, if no values are passed, the length of names will be 0, and we will print out nobody to greet:

      Output

      nobody to greet Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

      Using a variadic parameter can make your code more readable. Let’s create a function that joins words together with a specified delimiter. We’ll create this program without a variadic function first to show how it would read:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
          fmt.Println(line)
      
          line = join(",", []string{"Sammy", "Jessica"})
          fmt.Println(line)
      
          line = join(",", []string{"Sammy"})
          fmt.Println(line)
      }
      
      func join(del string, values []string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      In this program, we are passing a comma (,) as the delimiter to the join function. Then we are passing a slice of values to join. Here is the output:

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      Because the function takes a slice of string as the values parameter, we had to wrap all of our words in a slice when we called the join function. This can make the code difficult to read.

      Now, let’s write the same function, but we’ll use a variadic function:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      If we run the program, we can see that we get the same output as the previous program:

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      While both versions of the join function do the exact same thing programmatically, the variadic version of the function is much easier to read when it is being called.

      Variadic Argument Order

      You can only have one variadic parameter in a function, and it must be the last parameter defined in the function. Defining parameters in a variadic function in any order other than the last parameter will result in a compilation error:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      }
      
      func join(values ...string, del string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      This time we put the values parameter first in the join function. This will cause the following compilation error:

      Output

      ./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values

      When defining any variadic function, only the last parameter can be variadic.

      Exploding Arguments

      So far, we have seen that we can pass zero, one, or more values to a variadic function. However, there will be occasions when we have a slice of values and we want to send them to a variadic function.

      Let’s look at our join function from the last section to see what happens:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
      
          line = join(",", names)
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      If we run this program, we will receive a compilation error:

      Output

      ./join-error.go:10:14: cannot use names (type []string) as type string in argument to join

      Even though the variadic function will convert the parameter of values ...string to a slice of strings []string, we can’t pass a slice of strings as the argument. This is because the compiler expects discrete arguments of strings.

      To work around this, we can explode a slice by suffixing it with a set of ellipses (...) and turning it into discrete arguments that will be passed to a variadic function:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
      
          line = join(",", names...)
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      This time, when we called the join function, we exploded the names slice by appending ellipses (...).

      This allows the program to now run as expected:

      Output

      Sammy,Jessica,Drew,Jamie

      It’s important to note that we can still pass a zero, one, or more arguments, as well as a slice that we explode. Here is the code passing all the variations that we have seen so far:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      We now know how to pass zero, one, or many arguments, as well as a slice that we explode, to a variadic function.

      Conclusion

      In this tutorial, we have seen how variadic functions can make your code cleaner. While you won’t always need to use them, you may find them useful:

      • If you find that you’re creating a temporary slice just to pass to a function.
      • When the number of input params are unknown or will vary when called.
      • To make your code more readable.

      To learn more about creating and calling functions, you can read How to Define and Call Functions in Go.



      Source link

      How To Define and Call Functions in Go


      Introduction

      A function is a section of code that, once defined, can be reused. Functions are used to make your code easier to understand by breaking it into small, understandable tasks that can be used more than once throughout your program.

      Go ships with a powerful standard library that has many predefined functions. Ones that you are probably already familiar with from the fmt package are:

      • fmt.Println() which will print objects to standard out (most likely your terminal).
      • fmt.Printf() which will allow you to format your printed output.

      Function names include parentheses and may include parameters.

      In this tutorial, we’ll go over how to define your own functions to use in your coding projects.

      Defining a Function

      Let’s start with turning the classic “Hello, World!” program into a function.

      We’ll create a new text file in our text editor of choice, and call the program hello.go. Then, we’ll define the function.

      A function is defined by using the func keyword. This is then followed by a name of your choosing and a set of parentheses that hold any parameters the function will take (they can be empty). The lines of function code are enclosed in curly brackets {}.

      In this case, we’ll define a function named hello():

      hello.go

      func hello() {}
      

      This sets up the initial statement for creating a function.

      From here, we’ll add a second line to provide the instructions for what the function does. In this case, we’ll be printing Hello, World! to the console:

      hello.go

      func hello() {
          fmt.Println("Hello, World!")
      }
      

      Our function is now fully defined, but if we run the program at this point, nothing will happen since we didn’t call the function.

      So, inside of our main() function block, let’s call the function with hello():

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          hello()
      }
      
      func hello() {
          fmt.Println("Hello, World!")
      }
      

      Now, let’s run the program:

      You’ll receive the following output:

      Output

      Hello, World!

      Notice that we also introduced a function called main(). The main() function is a special function that tells the compiler that this is where the program should start. For any program that you want to be executable (a program that can be run from the command line), you will need a main() function. The main() function must appear only once, be in the main() package, and receive and return no arguments. This allows for program execution in any Go program. As per the following example:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          fmt.Println("this is the main section of the program")
      }
      

      Functions can be more complicated than the hello() function we defined. We can use for loops, conditional statements, and more within our function block.

      For example, the following function uses a conditional statement to check if the input for the name variable contains a vowel, then uses a for loop to iterate over the letters in the name string.

      names.go

      package main
      
      import (
          "fmt"
          "strings"
      )
      
      func main() {
          names()
      }
      
      func names() {
          fmt.Println("Enter your name:")
      
          var name string
          fmt.Scanln(&name)
          // Check whether name has a vowel
          for _, v := range strings.ToLower(name) {
              if v == 'a' || v == 'e' || v == 'i' || v == 'o' || v == 'u' {
                  fmt.Println("Your name contains a vowel.")
                  return
              }
          }
          fmt.Println("Your name does not contain a vowel.")
      }
      

      The names() function we define here sets up a name variable with input, and then sets up a conditional statement within a for loop. This shows how code can be organized within a function definition. However, depending on what we intend with our program and how we want to set up our code, we may want to define the conditional statement and the for loop as two separate functions.

      Defining functions within a program makes our code modular and reusable so that we can call the same functions without rewriting them.

      Working with Parameters

      So far we have looked at functions with empty parentheses that do not take arguments, but we can define parameters in function definitions within their parentheses.

      A parameter is a named entity in a function definition, specifying an argument that the function can accept. In Go, you must specify the data type for each parameter.

      Let’s create a program that repeats a word a specified number of times. It will take a string parameter called word and an int parameter called reps for the number of times to repeat the word.

      repeat.go

      package main
      
      import "fmt"
      
      func main() {
          repeat("Sammy", 5)
      }
      
      func repeat(word string, reps int) {
          for i := 0; i < reps; i++ {
              fmt.Print(word)
          }
      }
      

      We passed the value Sammy in for the word parameter, and 5 for the reps parameter. These values correspond with each parameter in the order they were given. The repeat function has a for loop that will iterate the number of times specified by the reps parameter. For each iteration, the value of the word parameter is printed.

      Here is the output of the program:

      Output

      SammySammySammySammySammy

      If you have a set of parameters that are all the same value, you can omit specifying the type each time. Let’s create a small program that takes in parameters x, y, and z that are all int values. We’ll create a function that adds the parameters together in different configurations. The sums of these will be printed by the function. Then we’ll call the function and pass numbers into the function.

      add_numbers.go

      package main
      
      import "fmt"
      
      func main() {
          addNumbers(1, 2, 3)
      }
      
      func addNumbers(x, y, z int) {
          a := x + y
          b := x + z
          c := y + z
          fmt.Println(a, b, c)
      }
      

      When we created the function signature for addNumbers, we did not need to specify the type each time, but only at the end.

      We passed the number 1 in for the x parameter, 2 in for the y parameter, and 3 in for the z parameter. These values correspond with each parameter in the order they are given.

      The program is doing the following math based on the values we passed to the parameters:

      a = 1 + 2
      b = 1 + 3
      c = 2 + 3
      

      The function also prints a, b, and c, and based on this math we would expect a to be equal to 3, b to be 4, and c to be 5. Let’s run the program:

      Output

      3 4 5

      When we pass 1, 2, and 3 as parameters to the addNumbers() function, we receive the expected output.

      Parameters are arguments that are typically defined as variables within function definitions. They can be assigned values when you run the method, passing the arguments into the function.

      Returning a Value

      You can pass a parameter value into a function, and a function can also produce a value.

      A function can produce a value with the return statement, which will exit a function and optionally pass an expression back to the caller. The return data type must be specified as well.

      So far, we have used the fmt.Println() statement instead of the return statement in our functions. Let’s create a program that instead of printing will return a variable.

      In a new text file called double.go, we’ll create a program that doubles the parameter x and returns the variable y. We issue a call to print the result variable, which is formed by running the double() function with 3 passed into it:

      double.go

      package main
      
      import "fmt"
      
      func main() {
          result := double(3)
          fmt.Println(result)
      }
      
      func double(x int) int {
          y := x * 2
          return y
      }
      
      

      We can run the program and see the output:

      Output

      6

      The integer 6 is returned as output, which is what we would expect by multiplying 3 by 2.

      If a function specifies a return, you must provide a return as part of the code. If you do not, you will receive a compilation error.

      We can demonstrate this by commenting out the line with the return statement:

      double.go

      package main
      
      import "fmt"
      
      func main() {
          result := double(3)
          fmt.Println(result)
      }
      
      func double(x int) int {
          y := x * 2
          // return y
      }
      
      

      Now, let’s run the program again:

      Output

      ./double.go:13:1: missing return at end of function

      Without using the return statement here, the program cannot compile.

      Functions exit immediately when they hit a return statement, even if they are not at the end of the function:

      return_loop.go

      package main
      
      import "fmt"
      
      func main() {
          loopFive()
      }
      
      func loopFive() {
          for i := 0; i < 25; i++ {
              fmt.Print(i)
              if i == 5 {
                  // Stop function at i == 5
                  return
              }
          }
          fmt.Println("This line will not execute.")
      }
      

      Here we iterate through a for loop, and tell the loop to run 25 iterations. However, inside the for loop, we have a conditional if statement that checks to see if the value of i is equal to 5. If it is, we issue a return statement. Because we are in the loopFive function, any return at any point in the function will exit the function. As a result, we never get to the last line in this function to print the statement This line will not execute..

      Using the return statement within the for loop ends the function, so the line that is outside of the loop will not run. If, instead, we had used a break statement, only the loop would have exited at that time, and the last fmt.Println() line would run.

      The return statement exits a function, and may return a value if specified in the function signature.

      Returning Multiple Values

      More than one return value can be specified for a function. Let’s examine the repeat.go program and make it return two values. The first will be the repeated value and the second will be an error if the reps parameter is not a value greater than 0:

      repeat.go

      package main
      
      import "fmt"
      
      func main() {
          val, err := repeat("Sammy", -1)
          if err != nil {
              fmt.Println(err)
              return
          }
          fmt.Println(val)
      }
      
      func repeat(word string, reps int) (string, error) {
          if reps <= 0 {
              return "", fmt.Errorf("invalid value of %d provided for reps. value must be greater than 0.", reps)
          }
          var value string
          for i := 0; i < reps; i++ {
              value = value + word
          }
          return value, nil
      }
      

      The first thing the repeat function does is check to see if the reps argument is a valid value. Any value that is not greater than 0 will cause an error. Since we passed in the value of -1, this branch of code will execute. Notice that when we return from the function, we have to provide both the string and error return values. Because the provided arguments resulted in an error, we will pass back a blank string for the first return value, and the error for the second return value.

      In the main() function, we can receive both return values by declaring two new variables, value and err. Because there could be an error in the return, we want to check to see if we received an error before continuing on with our program. In this example, we did receive an error. We print out the error and return out of the main() function to exit the program.

      If there was not an error, we would print out the return value of the function.

      Note: It is considered best practice to only return two or three values. Additionally, you should always return all errors as the last return value from a function.

      Running the program will result in the following output:

      Output

      invalid value of -1 provided for reps. value must be greater than 0.

      In this section we reviewed how we can use the return statement to return multiple values from a function.

      Conclusion

      Functions are code blocks of instructions that perform actions within a program, helping to make our code reusable and modular.

      To learn more about how to make your code more modular, you can read our guide on How To Write Packages in Go.



      Source link