You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Now, we can start making use of the struct in the main() function
The order of the fields can be used when defining a struct instance
However, this is not the recommended approach
funcmain() {
// Creating a value of type person// Using field order: This is not the recommended approachalex:=person{
"Alex", // firstName"Anderson", // lastName// Embedded struct// When embedding another struct, we can also skip the fieldName// We can just use the struct type// This has the effect of creating the fieldName the same name as the struct namecontactInfo{ // contactInfo"[email protected]", // email12345// zipCode
}
}
// Show the structfmt.Println(alex)
}
Instead of relying on the order of fields, use named fields
This is the recommended approach
funcmain() {
// Creating a value of type person// Using named fields: This is the recommended approachmaria:=person{
firstName: "Maria",
lastName: "Anderson",
// Embedded structcontact: contactInfo{
email: "[email protected]",
zipCode: 98765
}
}
// Show the structfmt.Println(maria)
}
We can also declare then assign later
The struct's fields would be assigned their zero-value by default before a new value is assigned to it
Data Type
Zero-Value
struct
null
string
""
int
0
float64
0
bool
false
funcmain() {
// Declare variable:// Fields default to null-valuevarjulieperson// To print a struct with key:value format, use %+vfmt.Printf("julie before assignment: %+v\n", julie)
// Assigning / Re-assigning valuesjulie.firstName="Julie"julie.lastName="Arkorius"julie.contact.email="[email protected]"julie.contact.zipCode=12345// Check againfmt.Printf("julie after assignment: %+v\n", julie)
}
Struct With Receiver Function
Similar to Types, we can define receiver functions to be attached to Structs as well
Note: The code below has some issues because of Pointers. We will review that below
// Receiver function for "person"func (pperson) print() {
fmt.Printf("%+v\n", p)
}
// This function has some issues because of pointerfunc (pperson) updateFirstName(newFirstNamestring) {
p.firstName=newFirstName
}
funcmain() {
// Defining a new person variablejim:=person{
firstName: "Jim",
lastName: "Patterson",
contact: contactInfo{
email: "[email protected]",
zipCode: 98765,
},
}
// Calling a receiver functionjim.print()
jim.updateFirstName("Jimmy")
jim.print()
// Why did it not update?// Because of how Pointers work
}
Pointers and Values
Previously, we saw that calling jim.updateFirstName() did not update jim.firstName field
Why did it not update the first name to "Jimmy"?
Because of Pointers in Go
A struct variable is a pointer type
Points to a value stored in memory
By default, Go is a pass-by-value language
With the way jim.updateFirstName() is defined, the value stored in jim is copied and stored in a new location in memory, which is then used by updateFirstName()
However, the original variable jim is still pointing to the old value
So, we are not updating the original struct but a copy of it in a different memory location
Posible Solution: We could re-assign back to the original variable
However, this is not preferred because it duplicates values in memory, thus inneficient
Also, if the struct is large, we have to move a large data 2 times in memory
Original -> Copy
Modify Copy
Copy -> Original
Actual Solution: To solve this issue, we need to use Pointers to force Go to pass-by-reference and update the original
Using Pointer
We use the &varName to create a reference (memory address) to varName
// JimPtr contains the memory address pointing to jimjimPtr:=&jim
We use the pointer type to pass to the function that wants to update the original variable
This requires the receiver function definition to change as well: It needs to take a Pointer-type as argument instead
Syntax
Decription
varName *type
A Pointer-type variable that points to a value of the type (i.e. contains the reference/memory address to the value of the type)
// updateFirstNamePointer takes a Pointer-type that points to a value of type personfunc (ptrPers*person) updateFirstNamePointer(newFirstNamestring) {
...
}
Within the receiver function definition, we de-reference the pointer (i.e. get back the value it is pointing at) using the *varName or (*varName) syntax
Syntax
Decription
*varName or (*varName)
Operator to access the value that exists at the memory address (pointer)
// Using pointer as receiver allows us to pass-by-referencefunc (ptrPers*person) updateFirstNamePointer(newFirstNamestring) {
// De-reference the pointer type// This means updating the value at the reference/pointer
(*ptrPers).firstName=newFirstName
}
However, Go can automatically de-reference a pointer as well
So the following also works
// Using pointer as receiver allows us to pass-by-referencefunc (ptrPers*person) updateFirstNamePointer(newFirstNamestring) {
// Go can automatically de-reference the pointerptrPers.firstName=newFirstName
}
Now, in main(), we can make use of this receiver function
We use the & sign to create a reference
Syntax
Decription
&varName or (&varName)
Operator to get the reference (memory address) that points to variable
funcmain() {
jim:=person{
firstName: "Jim",
lastName: "Patterson",
contact: contactInfo{
email: "[email protected]",
zipCode: 98765,
},
}
// Checking jim before calling a receiver functionjim.print()
// Updating jim: Passing By Reference
(&jim).updateFirstNamePointer("Jimmy")
// Checking jim after calling a receiver functionjim.print()
}
However, with Go, it is possible to substitute a pointer with its root variable
So the following still work, even if the receiver requires a pointer type
Make sure to define the parameter(s) of the receiver function to be of Pointer-type varName *type
Optional: Within the receiver function, we de-reference the pointer with *varName or *(varName) to access/update the value at the Pointer-type's address
But Go can automatically de-reference, so using just varName also works
Optional: When calling the receiver function (in main), pass a memory reference to it using &varName or &(varName)
But Go can automatically reference, so using just varName also works
About Pointers
Syntax
Decription
&varName or (&varName)
Operator to get the reference (memory address) that points to variable's value: This is Referencing a Pointer
*varName or (*varName)
Operator to access the value that exists at the memory address/pointer &varName: This is De-referencing a Pointer
varName
The original variable that we asigned the value to: Turns into its equivalent memory address reference using &
*type
A Pointer type that point to a memory address whose value is the type: &varName is of type *type
Same value as jonCopy but different objects: Original vs Copy
*person
Memory Address type referencing to a value of type person
Pointer Gotchas
Go is typically a Pass-By-Value language
However, Slices seems to by default passed-by-reference (Pointer) for better performance
NOTE:Arrays are passed-by-value though!!
In reality though, Go is always passed by value, but slices use a trick
When we create a slice, Go internally creates 2 separate data structure:
The Slice Data Structure
Pointer to the underlying array -> Points to a different address in memory
Capacity
Length
An underlying Array
Contains the actual list of items
When we modify the array, the Slice Data Structure gets moved around by value, but it is still pointing to the same underlying Array
The Slice Data Structure is passed around by value
But it always point to the underlying array
The Slice Data Structure gets modified (by value) when passed around
But the reference to the undelying array data structure remains
funcmain() {
mySlice:= []string{"Hi", "there", "how", "are", "you"}
myArray:= [4]string{"This", "is", "an", "Array"}
updateSlice(mySlice)
// Works because passed by reference// Updating on the argument = updating the same memory addressupdateArray(myArray)
// Does not work because passed by value// Updating on the argument = updating a different copied value// Need to use pointer to update the same variablefmt.Println(mySlice)
fmt.Println(myArray)
}
// Helper FunctionsfuncupdateSlice(s []string) {
s[0] ="Bye"
}
funcupdateArray(arr [4]string) {
arr[0] ="What"
}
List Of Data Structures That Are Passed BY REFERENCE (REFERENCE TYPES)
No need to use pointers with these
Slice
Map
Channel
Pointer
Function
List Of Data Structures That Are Passed BY VALUE (VALUES TYPES)
Use pointers to change the underlying values for these in functions