Tarkistetaan oppitunnin käyttöoikeutta...

Your First Go Program

Packages, Files, and Go Tools

Video ja luettava21 minEsikatselu

Katselet ilmaista esikatseluoppituntia.

Goal

By the end of this lesson, your program is split across several files and two packages. You use the Go tools that format your code and find mistakes in it.

What you will learn

  • How to split one package into several files
  • How to create your own package and import it
  • Exported and unexported names, which Go controls with capital letters
  • How to use go fmt and go vet

Why this matters

Tasker grows to dozens of files. Real Go projects keep code organized in packages, so each package has one clear job. You learn the rules now, while the program is small.

Step 1: Split main into two files

Open the hello folder from Lesson 1.1. Create a new file named footer.go:

package main

import "fmt"

func printFooter() {
	fmt.Println("Built with Go.")
}

Now change main.go so it calls this new function:

package main

import "fmt"

func main() {
	fmt.Println("Hello from Tasker!")
	printFooter()
}

Run the program:

go run .

Output:

Hello from Tasker!
Built with Go.

main.go uses printFooter, but it does not import footer.go. Both files start with package main and sit in the same folder, so they are one package. All files in a package can see each other's names.

Rule: one folder holds one package. Every file in the folder must use the same package name.

Step 2: Create your own package

Now you move the greeting into its own package. Create a folder named greeting inside hello. Inside it, create greeting.go:

package greeting

// Message returns a greeting for the given name.
func Message(name string) string {
	return "Hello, " + name + "!"
}

Message takes a name and returns a new text value. You learn the details of functions in Lesson 1.3. For now, read it as: "give me a name, and I give you a greeting."

Your folder now looks like this:

hello/
├── go.mod
├── main.go
├── footer.go
└── greeting/
    └── greeting.go

Step 3: Import your package

Change main.go:

package main

import (
	"fmt"

	"hello/greeting"
)

func main() {
	fmt.Println(greeting.Message("Tasker"))
	printFooter()
}

Run it:

go run .

Output:

Hello, Tasker!
Built with Go.

Look at the import path hello/greeting. It has two parts:

  • hello is the module name from go.mod.
  • greeting is the folder inside the module.

Go finds every package in your project this way: the module name, then the folder path.

When you import more than one package, put them in one import ( ... ) block. By convention, standard library packages come first. A blank line separates them from your own packages.

Step 4: Exported and unexported names

In main.go, change greeting.Message to greeting.message (with a small m). Run the program. Go stops with an error like this:

./main.go:10:23: undefined: greeting.message

Now change it back.

Go has a simple rule:

  • A name that starts with a capital letter is exported. Other packages can use it. Example: Message, Println.
  • A name that starts with a small letter is unexported. Only code inside the same package can use it. Example: printFooter.

Go has no public or private keywords. The first letter decides. This is why every function you call from fmt starts with a capital letter.

Notice the comment above Message. Go convention says each exported name has a comment that starts with the name itself. Go's documentation tools read these comments.

Step 5: Format your code with go fmt

Open greeting.go and make the formatting messy on purpose:

package greeting
// Message returns a greeting for the given name.
func Message(  name string  ) string {
return "Hello, "+name+"!"
     }

Now run:

go fmt ./...

Open the file again. Go fixed the spacing and indentation.

The pattern ./... means "this folder and every folder inside it." Many Go commands accept it.

Go has one official code style, and go fmt applies it. Every Go project looks the same, so you can read any Go code easily. Most code editors run go fmt each time you save a file.

Step 6: Find mistakes with go vet

Create a file named bad.go in the hello folder:

package main

import "fmt"

func showCount() {
	fmt.Printf("Count: %d\n", "ten")
}

%d asks Printf for a whole number, but the code gives it text. This code compiles, but the output would be wrong. Run:

go vet ./...

Output:

./bad.go:6:2: fmt.Printf format %d has arg "ten" of wrong type string

go vet finds code that compiles but is probably wrong. Delete bad.go when you are done.

Why it works: packages are Go's unit of code

  • A module is the whole project. It has one go.mod file.
  • A package is one folder of .go files with the same package name.
  • An import path is the module name plus the folder path.
  • The first letter of a name decides whether other packages can use it.

The compiler checks all of this. If you import a package and do not use it, or use a name that is not exported, the program does not compile.

Exercises

  1. Add a function. Add an exported function Farewell to the greeting package that returns "Goodbye, " + name + "!". Call it from main.
  2. Hide a helper. Add an unexported function exclaim to greeting that adds "!" to the end of a text value. Use it inside Message and Farewell. Then try to call greeting.exclaim from main. What happens?
  3. A second package. Create a package named banner with an exported function Title that returns "=== Tasker ===". Print it at the start of main.
  4. Break the folder rule. In greeting/greeting.go, change package greeting to package hello. Run the program and read the error. Change it back.
  5. Vet it. Use fmt.Printf("Name: %s\n", 42) somewhere in your code. Run go vet ./.... Then fix the line.

Checkpoint

You are ready for the next lesson if you can:

  • Explain the difference between a module and a package
  • Create a package in a new folder and import it with the correct path
  • Explain how Go decides if a name is exported
  • Use go fmt ./... and go vet ./... and explain what each one does