Go maintains its unique spin on flexibility with implicit and explicit definitions for functions, methods and interfaces. The main(){}
function in the main package is where our code must always start.
While Go normally enforces the use of every package and variable, you can actually include empty functions that aren’t called and your code will still compile. Calling a function is also straightforward.
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
helloFromFunc()
}
func emptyFunc() {
}
func helloFromFunc() {
fmt.Println("Hello from a function!")
}
/* Program Output:
Hello World!
Hello from a function!
*/
Arguments and parameters are easily passed in Go. Arguments are in the function invocation call, and parameters are in the function definition. Coding lingo!
func main() {
// define arguments
firstNameArg := "Izzy"
lastNameArg := "Miles"
printFullName(firstNameArg, lastNameArg)
}
func printFullName(firstNameParam string, lastNameParam string) {
fmt.Println(firstNameParam, lastNameParam)
}
Return types of functions can be defined to return single values or tuples of varying types. The return types are defined following the parameter definitions. I hope you know of Harry Potter or else this function is going to look like Latin to you.
package main
import "fmt"
func main() {
ronWeasley := "Ron Weasley"
canCastSpell := wingardiumLeviosa(ronWeasley)
if canCastSpell {
fmt.Println("Levitating object...")
} else {
fmt.Println("Can't levitate, it was probably Ron...")
}
}
func wingardiumLeviosa(wizard string) (bool) { // bool return type
if wizard == "Ron Weasley" {
fmt.Println("It's levi-OSA, not levio-SA!")
return false
} else {
return true
}
}
It’s a common Go practice to always return an error value for an operation that could possibly go wrong. For example, say we are trying to open a file. If we can open the file no problem, we would return a nil
in place of an error, meaning that everything went fine. If something goes wrong, we want to signal that and handle the error.
**Note: **The below code would require you to import the "os"
package.
func openFile (filename string) (error) {
f, err := os.Open("filename.txt")
if err != nil {
log.Fatal(err)
return err
}
// do something with the open *File f
return nil
}
This is a bit repetitive, since the os.Open()
function actually returns a file and an error as well. This also isn’t very graceful since we just crash the program if it fails. The main point is that we want functions that could go wrong to signal exactly why they did go wrong, and errors help achieve that.
Methods are different from functions in Go in the basic sense that methods have to do with objects. Go is slightly different than other languages because you actually just pass an object to a function to make it a method, as compared to Python where you define a function as part of a class to make it a method. Reminder, there are no classes in Go, only structs and interfaces!
package main
import "fmt"
func main() {
hp := NewWizard("Harry Potter", 12)
hp.learnSpell("Wingardium Leviosa")
fmt.Println(hp.Name, "knows how to cast", hp.KnownSpells)
}
type Wizard struct {
Name string
Age int
KnownSpells []string
}
// This function returns a new instance of a Wizard
func NewWizard(name string, age int) *Wizard {
return &Wizard{ // notice no explicit constructor
Name: name,
Age: age,
}
}
// we pass a wizard to learnSpell create a method
func (w *Wizard) learnSpell (spell string) {
w.KnownSpells = append(w.KnownSpells, spell)
}
It’s a common Go practice to return a reference to the new struct instance, which is why the return type of NewWizard()
is a pointer *Wizard
.
Interfaces in Go are a bit different as compared to other languages, but still very powerful in their application. Essentially, an interface gives us a contract of features to implement. Let’s take shapes for an example where we just need to implement area and perimiter methods.
type shape interface {
area() float64
perim() float64
}
Simple enough. In most other common languages, you would instantiate some class that would include a keyword to implement this interface. Instead, Go will assume you implemented an interface so long as you define methods for an object that are equivalent in types.
type pentagon struct {
side float64
}
func (p pentagon) area() float64 {
radicand := 5*(5+(2*math.Sqrt(5)))
return (0.25)*math.Sqrt(radicand)*math.Pow(p.side,2)
}
func (p pentagon) perim() float64 {
return 5*p.side
}
There you have it, since we implemented the area()
and perim()
methods we have thus implemented the shape
interface itself! Also, you learned how to find the area of a pentagon. Now that’s big brain time.
I hope you enjoyed this tutorial on functions, methods and interfaces in Go. If there was something you enjoyed or a topic that you want to hear more about, please leave a comment below! Thanks for reading.
This article was originally published on levelup.gitconnected.com
#golang #coding #software-development #go