blob: 8100c080aa5f6a044cd50e6e8e55dd0effe2e66f [file] [log] [blame]
<!--{
"Title": "Return greetings for multiple people",
"Path": "/doc/tutorial/greetings-multiple-people"
}-->
<p>
In the last changes you'll make to your module's code, you'll add support for
getting greetings for multiple people in one request. In other words, you'll
handle a multiple-value input and pair values with a multiple-value output.
</p>
<aside class="Note">
<strong>Note:</strong> This topic is part of a multi-part tutorial that begins
with <a href="create-module.html">Create a Go module</a>.
</aside>
<p>
To do this, you'll need to pass a set of names to a function that can return a
greeting for each of them. Changing the <code>Hello</code> function's
parameter from a single name to a set of names would change the function
signature. If you had already published the <code>greetings</code> module and
users had already written code calling <code>Hello</code>, that change would
break their programs. In this situation, a better choice is to give new
functionality a new name.
</p>
<p>
In the last code you'll add with this tutorial, update the code as if you've
already published a version of the <code>greetings</code> module. Instead of
changing the <code>Hello</code> function, add a new function
<code>Hellos</code> that takes a set of names. Then, for the sake of
simplicity, have the new function call the existing one. Keeping both
functions in the package leaves the original for existing callers (or future
callers who only need one greeting) and adds a new one for callers that want
the expanded functionality.
</p>
<ol>
<li>
In greetings/greetings.go, change your code so it looks like the following.
<pre>
package greetings
import (
"errors"
"fmt"
"math/rand"
"time"
)
// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
// If no name was given, return an error with a message.
if name == "" {
return name, errors.New("empty name")
}
// Create a message using a random format.
message := fmt.Sprintf(randomFormat(), name)
return message, nil
}
<ins>// Hellos returns a map that associates each of the named people
// with a greeting message.
func Hellos(names []string) (map[string]string, error) {
// A map to associate names with messages.
messages := make(map[string]string)
// Loop through the received slice of names, calling
// the Hello function to get a message for each name.
for _, name := range names {
message, err := Hello(name)
if err != nil {
return nil, err
}
// In the map, associate the retrieved message with
// the name.
messages[name] = message
}
return messages, nil
}</ins>
// Init sets initial values for variables used in the function.
func init() {
rand.Seed(time.Now().UnixNano())
}
// randomFormat returns one of a set of greeting messages. The returned
// message is selected at random.
func randomFormat() string {
// A slice of message formats.
formats := []string{
"Hi, %v. Welcome!",
"Great to see you, %v!",
"Hail, %v! Well met!",
}
// Return one of the message formats selected at random.
return formats[rand.Intn(len(formats))]
}
</pre>
<p>
In this code, you:
</p>
<ul>
<li>
Add a <code>Hellos</code> function whose parameter is a slice of names
rather than a single name. Also, you change one of its return types from
a <code>string</code> to a <code>map</code> so you can return names
mapped to greeting messages.
</li>
<li>
Have the new Hellos function call the existing Hello function. This
leaves both functions in place.
</li>
<li>
Create a <code>messages</code>
<a href="https://blog.golang.org/maps">map</a> to associate each of the
received names (as a key) with a generated message (as a value). In Go,
you initialize a map with the following syntax:
<code>make(map[<em>key-type</em>]<em>value-type</em>)</code>. You have
the <code>Hellos</code> function return this map to the caller.
</li>
<li>
Loop through the names your function received, checking that each has a
non-empty value, then associate a message with each. In this
<code>for</code> loop, <code>range</code> returns two values: the index
of the current item in the loop and a copy of the item's value. You
don't need the index, so you use the Go
<a href="https://golang.org/doc/effective_go.html#blank"
>blank identifier (an underscore)</a
>
to ignore it.
</li>
</ul>
</li>
<li>
In your hello/hello.go calling code, pass a slice of names, then print the
contents of the names/messages map you get back.
<p>
In hello.go, change your code so it looks like the following.
</p>
<pre>
package main
import (
"fmt"
"log"
"example.com/greetings"
)
func main() {
// Set properties of the predefined Logger, including
// the log entry prefix and a flag to disable printing
// the time, source file, and line number.
log.SetPrefix("greetings: ")
log.SetFlags(0)
<ins>// A slice of names.
names := []string{"Gladys", "Samantha", "Darrin"}</ins>
// Request greeting messages for the names.
messages, err := greetings.Hellos(names)
if err != nil {
log.Fatal(err)
}
// If no error was returned, print the returned map of
// messages to the console.
fmt.Println(messages)
}
</pre>
<p>
With these changes, you:
</p>
<ul>
<li>
Create a <code>names</code> variable as a slice type holding three
names.
</li>
<li>
Pass the <code>names</code> variable as the argument to the
<code>Hellos</code> function.
</li>
</ul>
</li>
<li>
At the command line, change to the directory that contains hello/hello.go,
then run hello.go to confirm that the code works.
<p>
The output should be a string representation of the map associating names
with messages, something like the following:
</p>
<pre>
$ go run hello.go
map[Darrin:Hail, Darrin! Well met! Gladys:Hi, Gladys. Welcome! Samantha:Hail, Samantha! Well met!]
</pre
>
</li>
</ol>
<p>
This topic introduced maps for representing name/value pairs. It also
introduced the idea of
<a href="https://blog.golang.org/module-compatibility"
>preserving backward compatibility</a
>
by implementing a new function for new or changed functionality in a module.
In the tutorial's <a href="add-a-test.html">next topic</a>, you'll use
built-in features to create a unit test for your code.
</p>
<p class="Navigation">
<a class="Navigation-prev" href="random-greeting.html"
>&lt; Return a random greeting</a
>
<a class="Navigation-next" href="add-a-test.html">Add a test &gt;</a>
</p>