For example, you can write fairly complicated programs using just a single .go file. Good coding practice is to put your reusable functions into a library (or in Go, a package).
I'm assuming a file structure similar to this for your workspace:
Go
|
|-bin
|-pkg
|-src
Now, let's create a small main() Go file.
Go
|
|-bin
|-pkg
|-src
|-uses_a_package
|-uses_a_package.go
I have a personal habit when creating files to test out theories or implementations of using the word "test" in the names. Go language developers frown on this practice because "test" is used as a literal code testing reserved word; one of the things I personally find screwy about the language, but it is what it is. If you put something like "uses_a_package_test.go" as a filename, you're in for some head scratching and Google searches leading you to articles about running application tests.
So in the file /go/src/uses_a_package/uses_a_package.go I put the following:
package main import ( "fmt" "package_demo" ) func main() { // main() says hello fmt.Println("Hello from main()!") // Package, say hello package_demo.Howdy() }
Now I need the package. I create a new directory and .go file:
Go
|
|-bin
|-pkg
|-src
|-uses_a_package
|-uses_a_package.go
|-package_demo
|-package_demo.go
In package_demo.go I enter the following:
// Package package_demo is demonstrating how to make a simple package file/library package package_demo import ( "fmt" ) // Howdy() says hello with no arguments and no value returned func Howdy() { // Prints a hello to the console fmt.Println("Hello from the package_demo package!") return }
Next I compile the everything. From /go/src/uses_a_package, I run:
go install
I didn't have to separately compile the package. Why?
The workspace now looks like this:
Go
|
|-bin
|-uses_a_package
|-uses_a_package
|-pkg
|-darwin_amd64
|package_demo.a
|-darwin_amd64
|package_demo.a
|-src
|-uses_a_package
|-uses_a_package.go
|-package_demo
|-package_demo.go
The answer is when I ran the "install" command, Go saw the extra package import and found that it, too, needed compilation. I can run the install command just on the package and it'll compile and insert it into my /go/pkg directory, but I didn't need to.
Running the application:
../../bin/uses_a_package
...yields the following output:
Hello from main()!
Hello from the package_demo package!
The way I added comments to the package is kind of important. It's another bit of knowledge that isn't necessarily intuitive, but you run across it in trying to get other things working in Go; the self-documenting feature of GoDoc.
From /go/src, I run:
godoc ./package_demo
Output is:
PACKAGE DOCUMENTATION
package package_demo
import "./package_demo"
Package package_demo is demonstrating how to make a simple package
file/library
FUNCTIONS
func Howdy()
Howdy() says hello with no arguments and no value returned
The answer is when I ran the "install" command, Go saw the extra package import and found that it, too, needed compilation. I can run the install command just on the package and it'll compile and insert it into my /go/pkg directory, but I didn't need to.
Running the application:
../../bin/uses_a_package
...yields the following output:
Hello from main()!
Hello from the package_demo package!
The way I added comments to the package is kind of important. It's another bit of knowledge that isn't necessarily intuitive, but you run across it in trying to get other things working in Go; the self-documenting feature of GoDoc.
From /go/src, I run:
godoc ./package_demo
Output is:
PACKAGE DOCUMENTATION
package package_demo
import "./package_demo"
Package package_demo is demonstrating how to make a simple package
file/library
FUNCTIONS
func Howdy()
Howdy() says hello with no arguments and no value returned
There is definitely room for improvement in how I documented things with comments, but you get the point. Properly comment your code in Go!
I should also note that GoDoc is used for the documentation on the main Go site. Visit that to get an idea how to properly document your functions and code...as I'm throwing together quick demo stuff for this, you can see I say something stupidly obvious like "no arguments and no return value."
Now, why is GoDoc run on the directory, and not on the file? For one, it doesn't work.
godoc ./package_demo/package_demo.go
2015/07/31 13:44:27 newDirectory(/target): not a package directory
Two, the directory acts like the package, and the files in it are the goodies. Let's try something...
in /go/src/package_demo, I create a new file called package_demo2.go and populate it with the following text:
// Package package_demo2 takes the package demonstration one step farther, showing one package // directory is "merged" into one package despite multiple sub-files package package_demo import ( "fmt" ) // CustomHello takes a name passed as a parameter and prints a greeting to that string func CustomHello(strName string) { // Print a custom hello! fmt.Println("Hello, " + strName + "!") return }
...and add a call in main() to the added function:
package main import ( "fmt" "package_demo" ) func main() { // main() says hello fmt.Println("Hello from main()!") // Package, say hello package_demo.Howdy() // Second package file, say hello to Bob! package_demo.CustomHello("Bob") }
Compile, and run:
../../bin/uses_a_package
Hello from main()!
Hello from the package_demo package!
Hello, Bob!
At this point the directory structure looks like this:
Go
|
|-bin
|-uses_a_package
|-uses_a_package
|-pkg
|-darwin_amd64
|package_demo.a
|-darwin_amd64
|package_demo.a
|-src
|-uses_a_package
|-uses_a_package.go
|-package_demo
|-package_demo.go
|-package_demo2.go
|-package_demo2.go
// Package package_demo is demonstrating how to make a simple package file/library package package_demo import ( "fmt" ) // Howdy() says hello with no arguments and no value returned func Howdy() { // Prints a hello to the console fmt.Println("Hello from the package_demo package!") return } // HelloBack will return a string with a greeting to the caller rather than printing directly to the console func HelloBack(strName string) string { // Who to say hello to? strName = "Hello there, " + strName + "!" return(strName) }
...followed by a call added to uses_a_package.go:
package main import ( "fmt" "package_demo" ) func main() { // main() says hello fmt.Println("Hello from main()!") // Package, say hello package_demo.Howdy() // Second package file, say hello to Bob! package_demo.CustomHello("Bob") // Back to the first file... fmt.Println(package_demo.HelloBack("Phil")) }
Then compile and test!
go install
../../bin/uses_a_package
Hello from main()!
Hello from the package_demo package!
Hello, Bob!
Hello there, Phil!
And of course GoDoc is helpful with documentation. From go/src, I ran "godoc ./package_demo":
PACKAGE DOCUMENTATION
package package_demo
import "./package_demo"
Package package_demo is demonstrating how to make a simple package
file/library
Package package_demo2 takes the package demonstration one step farther,
showing one package directory is "merged" into one package despite
multiple sub-files
FUNCTIONS
func CustomHello(strName string)
CustomHello takes a name passed as a parameter and prints a greeting to
that string
func HelloBack(strName string) string
HelloBack will return a string with a greeting to the caller rather than
printing directly to the console
func Howdy()
Howdy() says hello with no arguments and no value returned
One thing to note is functions to be exported in the package...visible to the executable main()...must be capitalized. An edit to our growing package_demo.go file:
// Package package_demo is demonstrating how to make a simple package file/library package package_demo import ( "fmt" ) // Howdy() says hello with no arguments and no value returned func Howdy() { // Prints a hello to the console fmt.Println("Hello from the package_demo package!") return } // HelloBack will return a string with a greeting to the caller rather than printing directly to the console func HelloBack(strName string) string { // Who to say hello to? strName = "Hello there, " + strName + "!" return(strName) } // CallsFuncInPackageSelf takes a name passed as an argument and calls another function within the package // source, a non-exported (capitalized) function, to construct a reply func CallsFuncInPackageSelf(strName string) { strResponse := constructString(strName) fmt.Println(strResponse) } // constructString crafts a greeting and returns a reply, but only works within the package func constructString(strGreetMe string) string { return("A secret hello to " + strGreetMe + "!") }
You'll notice I now added a CallsFuncInPackageSelf function, which when invoked calls "constructString" (lowercase first letter) to return a secret hello to the name that was passed to CallsFuncInPackageSelf. In main() I make a small addition:
package main import ( "fmt" "package_demo" ) func main() { // main() says hello fmt.Println("Hello from main()!") // Package, say hello package_demo.Howdy() // Second package file, say hello to Bob! package_demo.CustomHello("Bob") // Back to the first file... fmt.Println(package_demo.HelloBack("Phil")) // Now a secret greeting... package_demo.CallsFuncInPackageSelf("Tom") }
../../bin/uses_a_package
Hello from main()!
Hello from the package_demo package!
Hello, Bob!
Hello there, Phil!
A secret hello to Tom!
But if I edit uses_a_package.go to directly call constructString("Tom") and try a recompile, I get this:
go install
# uses_a_package
./uses_a_package.go:26: cannot refer to unexported name package_demo.constructString
./uses_a_package.go:26: undefined: package_demo.constructString
In order for the function to be visible outside the package, it must begin with a capital letter. That's why when you're working with methods like fmt.Println() the function has a capital...in this case, it's exported from the fmt package!
I hope this is helpful in getting started creating packages in Go!
No comments:
Post a Comment