Defining Simple Functions¶
A Go function consists of a signature (or header) followed by a block of code that will be executed when the function is called.
Here’s a function that prints a greeting message:
func hello(name string) { // function signature
fmt.Println("Hello " + name + "!") // function body
}
// Calling hello("Dave") prints: Hello Dave!
This function’s name is hello
, and it takes as input a single parameter of
type string
called name
. Notice that the name of the parameter comes
first, followed by its data type. It prints a message to the screen without
returning a value.
In Go, function parameters are always passed by value. For example,
calling f(x)
means that the value of x
is copied. Thus, you cannot
modify the value of x
, and, if x
is big, the copying takes extra time
and space.
Consider this function for calculating the area of a rectangle:
func rect_area(width, height int) int { // function signature
return width * height // function body
}
// rect_area(10, 5) returns 50
rect_area
takes two int
parameters, width
and height
. It also
returns an int
: the return
statement is used to end the function and
return a value.
You could also have written the function with a slightly different header like this:
func rect_area(width int, height int) int {
return width * height
}
Here’s a function that counts the number of e characters in a string:
func ecount(s string) int {
result := 0
for _, c := range s {
if c == 'e' || c == 'E' {
result++
}
}
return result
}
As before, a return
statement ends a function and returns a value. Another
way to write this function is with a named result parameter, e.g.:
func ecount(s string) (result int) { // result is initialized to 0
for _, c := range s {
if c == 'e' || c == 'E' {
result++
}
}
return // no need to mention result here
}
Functions can return multiple values. A common use case for this is to return a value and an error flag. For instance:
import "os/ioutil"
// ...
func ecountFile(fname string) (int, error) { // note two return types
bytes, err := ioutil.ReadFile(fname) // bytes is a byte slice
result := 0
if err == nil { // err is nil when file
for _, c := range bytes { // is read correctly
if c == 'e' || c == 'E' {
result++
}
}
}
return result, err
}
error
is a built-in type, and ioutil.ReadFile
is a convenience
function that reads an entire file from disk into a slice of bytes.
You can call ecountFile
like this:
count, err := ecountFile("funcs.txt")
if err == nil {
fmt.Println(count)
} else {
fmt.Println("unable to read funcs.txt")
}
It’s up to the programmer to check the error flag.
We could also have written ecountFile
with named result parameters:
func ecountFile(fname string) (result int, err error) { // named results
bytes, err := ioutil.ReadFile(fname)
if err == nil {
for _, c := range bytes {
if c == 'e' || c == 'E' {
result++
}
}
}
return
}
Questions¶
- Describe the technique Go uses to pass parameters to functions.
- True or false: every Go function must have an explicitly named type
for its return value. For functions that don’t return a value, the return
type is
void
. - True or false: when a Go function returns more than one value, the values must be of the same type.
- True or false: Go lets you use both multiple return values and named result parameters in the same function.