Since starting to play with golang I’ve run into a couple of interesting items I thought worth writing about. For those of you that are seasoned developers, I assure you, this wont be interesting. But for us that are getting started this might be worth reading.
Pointers
Nothing super exciting here if you’ve used them in other languages but it’s worth talking about since it can be confusing. Pointers are really just a way for us to gain access to the ‘real’ variable when you aren’t in the function that defines it. Put another way, when you call a function that takes a variable, you are only giving that function a copy of the variable, not the real variable. Pointers allow us to reference the actual location in memory where the value is stored rather than the value itself. Examples always make this more clear. Take for instance this example of code…
package main import "fmt" func main() { //Define myname and set it to 'jonlangemak' myname := "jonlangemak" //Rename without using pointers rename(myname) fmt.Println(myname) //Rename using pointers pointerrename(&myname) fmt.Println(myname) } //Function without pointers func rename(myname string) { myname = "reallycooldude" } //Function with pointers func pointerrename(myname *string) { *myname = "reallycooldude" }
If we run this sample code, the output we’ll get will look like this…
jonlangemak reallycooldude
I define two functions, one called ‘rename’ that takes a variable called ‘myname’ as a type of string. The ‘pointerrename’ function takes a variable called ‘myname’ which is a type int pointer. We denote the pointer by using a ‘*’. Note that in the main function when I want to pass the pointer to the function I use a ‘&’. The ‘&’ tells golang to find the location of the variable and pass the pointer to the function which is expecting to receive a pointer of type int.
Structs
Structs allow you to sort of create you own variable type. And when I say your own variable type, I mean more like a combination of multiple variables into on. Take for instance this example…
package main import "fmt" //Define a struct of type indexcard type indexcard struct { name string age int } func main() { //Create a var of type indexcard var jon indexcard //Set the name jon.name = "Jon Langemak" fmt.Println(jon) //Create a var of type indexcard and populate it immediately bob := indexcard{"Bob Bobster", 55} fmt.Println(bob) }
The output from this program will be…
{Jon Langemak 0} {Bob Bobster 55}
So they’re sort of nice way to keep track of data that falls into key/value pairs.
Methods
Once you have a struct, you can define a method associated with it. What distinguishes methods from functions is the addition of defining the method type in the function. Take this example for instance…
package main import "fmt" type indexcard struct { name string age int } func main() { bob := indexcard{"Bob Bobster", 55} fmt.Println("Bob is currently ", bob.age, " years old") fmt.Println(bob.age) fmt.Println("If we make Bob older he is ", bob.makeolder(), " years old") bob.makeyounger() fmt.Println("If we make Bob younger he is ", bob.age, " years old") } //Method to make Bob younger without a return func (person *indexcard) makeyounger() { person.age -= 15 } //Method to make Bob older with a return func (person *indexcard) makeolder() int { person.age += 5 return person.age }
Here we have the same struct called ‘indexcard’ but we also have a couple of methods related to the construct. Notice that these methods define the type of struct they are receiving, a variable named person of type ‘indexcard’. Notice that the ‘person’ variable is a pointer reference. Also note that we aren’t using the ‘&’ when we call the function from the main function. Golang is smart enough to know to do the pointer magic when calling methods.
So what does the output of this look like?…
Bob is currently 55 years old If we make Bob older he is 60 years old If we make Bob younger he is 45 years old
As you can see, we called the methods in two different manners from the main function. To make Bob older we called the method and the function call returns an integer based on adding 5 to Bob’s existing age. To make Bob younger we call the method directly. The method ‘makeyounger’ is not a return function and instead updates the pointer directly. When back in the main function we print Bob’s age again we get his much younger age of 45.
Slices
Slices are sort of the array of golang. I mean – they’re really sort of a wrapper around an array. The only difference between an array and a slice in golang is you define the size of an array whereas a slice you don’t define the size. There’s tons of info out there on the differences but from what I can discern, that’s really it. So that being said, here’s a quick example…
package main import "fmt" func main() { //Define and populate a slice called slice1 slice1 := []string{"jon", "bob", "bill", "ted", "mark"} fmt.Println(slice1) //Initialize a slice called slice 2 of type string slice2 := []string{} //Add data to the slice slice2 = append(slice2, "jon", "bob") fmt.Println(slice2) //Add more data to the slice slice2 = append(slice2, "bill", "ted", "mark") fmt.Println(slice2) //Return a specific value of the slice fmt.Println(slice2[:1]) }
Slices work much like arrays do but ,in my opinion, are easier to work with.
Embedded Types
Embedded types are a way to embed a struct within another struct. When you do this, you can reference values of the embedded struct directly. An example will clear up any remaining confusion…
package main import "fmt" //Create a struct that's composed of 2 other structs type indexcard struct { person house } //Create the embedded struct person type person struct { name string age int } //Create the embedded struct house type house struct { address string state string zipcode string } func main() { //Create a var of type indexcard var me indexcard me.name = "Bob" //Items for either of the embedded structs can be set directly me.age = 21 //Methods can be created against the struct just like normal methods me.sethouse("1111 Berry Lane", "Minnesota", "55555") me.printall() } //Method that receives input and sets data in the indexcard struct, no return func (input *indexcard) sethouse(newaddress string, newstate string, newzip string) { input.address = newaddress input.state = newstate input.zipcode = newzip } //Method that prints data from the indexcard passed to the method func (input *indexcard) printall() { fmt.Println("The whole address card") fmt.Println("Name:", input.name) fmt.Println("Age:", input.age) fmt.Println("Address:", input.address) fmt.Println("State:", input.state) fmt.Println("Zip:", input.zipcode) }
So not too much new here, just showing that we can create structs that are composed of other structs.
That’s all for tonight, more this weekend hopefully.