Structs and Methods
Define a struct (Uppercase if it should be accessible between other packages/files):
type user struct {
}
// Define and initialize
type user struct {
firstName string
lastName string
birthdate string
}Instantiating Structs
type user struct {
firstName string
lastName string
birthdate string
createdAt time.Time
}
// Instantiating
var appUser user
appUser = user{
firstName: userFirstName,
lastName: userLastName,
birthdate: userBirthdate,
createdAt: time.Now(),
}
// Instantiating with shorthand
appUser := user{
firstName: userFirstName,
lastName: userLastName,
birthdate: userBirthdate,
createdAt: time.Now(),
}Passing Struct Values as Arguments
func outputUserDetails(u user) {
fmt.Println(u.firstName, u.lastName, u.birthdate, u.createdAt)
}Creating Struct Methods
Typically, the method is written underneath the struct to keep the codebase easy to read.
Example:
type Person struct {
firstName string
lastName string
age int
}
func (p Person) outputPersonData() {
fmt.Printf("%s is currently %d years old.\n", p.firstName, p.age)
}With this method, we can call it in the main function just by using it on the name of our struct:
personOne.outputPersonData()We can also use pointers to avoid duplicates by dereferencing the structure type inside the first parentheses, this will mutate the data directly without creating a copy of it.
func (p *Person) clearPersonData() {
p.firstName = "None"
}We can also create a constructor function to make the initialization easier:
func newPerson(firstname string, lastname string, age int) *Person {
return &Person{
firstName: "Berkay",
lastName: "Kaya",
age: 25,
}
}
// inside main function:
personOne := newPerson("Berkay", "Kaya", 25)But we can also use one more thing when we create a constructor function! We can add conditions to it. If we don't want that e.g. the first name and last name can be empty we can put our condition in.
func newPerson(firstname string, lastname string, age int) (*Person, error) {
if firstname == "" || lastname == "" {
return nil, errors.New("first and Last Name cannot be empty")
}
return &Person{
firstName: firstname,
lastName: lastname,
age: age,
}, nil
}
personOne, err := newPerson("Beko", "", 19)
if err != nil {
fmt.Println(err)
}Output:
first and Last Name cannot be empty
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0x27b076]Structs, Packages & Exports
You can use structs and there methods inside your main package while writing the code in another package. For that, the struct and the methods name must all begin with a capitalized letter!
package person
import (
"errors"
"fmt"
)
type Person struct {
firstName string
lastName string
age int
}
func (p Person) OutputPersonData() {
fmt.Printf("%s is currently %d years old.\n", p.firstName, p.age)
}
func (p *Person) ClearPersonData() {
p.firstName = "None"
}
func NewPerson(firstname string, lastname string, age int) (*Person, error) {
if firstname == "" || lastname == "" {
return nil, errors.New("first and Last Name cannot be empty")
}
return &Person{
firstName: firstname,
lastName: lastname,
age: age,
}, nil
}Then we can use the structure and its methods in our main file:
package main
import (
"fmt"
"learning/structs/person"
)
func main() {
personOne, err := person.NewPerson("Berkay", "Kaya", 25)
if err != nil {
fmt.Println(err)
}
personOne.OutputPersonData()
personOne.ClearPersonData()
personOne.OutputPersonData()
}Struct Embedding
Thea idea of struct embedding is that you simply build a new struct that builds up on an existing struct.
For example, we have her the Person struct with a method:
type Person struct {
firstName string
lastName string
age int
}
func (p Person) OutputPersonData() {
fmt.Printf("%s is currently %d years old.\n", p.firstName, p.age)
}What if we want to add another struct which has access to the attributes and methods of the person struct? This would make sense for example if we would create an Administrator which would also require first and last name with some additional fields like email and phone number.
type Admin struct {
email string
phone string
Person Person
}As you can see we can expose the Person struct inside our Admin struct. We then can create a new constructor for the Admin struct:
func NewAdmin(email string, phone string, firstName string, lastName string, age int) Admin {
return Admin{
email: email,
phone: phone,
Person: Person{
firstName: firstName,
lastName: lastName,
age: 40,
},
}
}Because our Person struct is now inside the Admin struct, we can directly access the methods from the Person struct:
// Use our newAdmin constructor to create a new Administrator
localAdmin := person.NewAdmin("[email protected]", "+442344322", "Harold", "Thinhair", 40)
// Use the method OutputPersonData which we wrote for the Person struct
localAdmin.Person.OutputPersonData()If we expose the Person struct without its type Person again, we can use the method directly without adding the .Person notation.
This would look like this:
// Anonymous Embedding
type Admin struct {
email string
phone string
Person
}
// Access its methods directly without using the .Person notation:
localAdmin.OutputPersonData()Last updated