Go has no classes, but it has structs and methods
If you come from a language with classes, the first surprise is that Go has none. There is no class keyword, no this and no built-in constructor. What Go gives you instead is smaller: a struct groups named fields into one type, and a method is a function that is attached to a type. Together they cover what most programs need from classes.
Defining a struct
A struct type is declared with type and struct. Each field is written as its name followed by its type, with no colon and no semicolon:
type Employee struct {
name string
age int
}
The struct is the blueprint. A value of the type is one concrete employee with actual data.
Creating values
A variable of a struct type starts with every field set to its zero value, which is "" for a string and 0 for an int:
var emp1 Employee
emp1.name = "Andrew"
fmt.Println(emp1.name)
Usually you set the fields at creation with a struct literal, naming each field inside braces:
emp2 := Employee{name: "Mary", age: 25}
emp3 := &Employee{name: "Ann", age: 41}
The second form, with &, gives a pointer to a new struct. Fields are read and written with the dot operator on both a value and a pointer. Go dereferences the pointer for you, so emp3.age works without any extra syntax.
Go does not provide constructors. The convention is an ordinary function whose name starts with New, which builds and returns a value:
func NewEmployee(name string, age int) Employee {
return Employee{name: name, age: age}
}
Methods and receivers
A method is a function with one extra parameter, the receiver, written in parentheses before the method name. The receiver replaces this, but you name it yourself, and you must always use it to reach the fields:
func (e Employee) printName() {
fmt.Println(e.name)
}
You call it with the dot: emp2.printName(). Writing just name inside the method would not refer to the field. It would be an undefined variable.
Value receivers and pointer receivers
A value receiver, (e Employee), gets a copy of the struct, so changes inside the method do not reach the caller. A pointer receiver, (e *Employee), gets the address of the original and can change it:
func (e Employee) birthdayCopy() { e.age++ }
func (e *Employee) birthday() { e.age++ }
emp := Employee{name: "Andrew", age: 30}
emp.birthdayCopy()
fmt.Println(emp.age)
emp.birthday()
fmt.Println(emp.age)
30
31
The first call changed only a copy. The second changed emp itself. Go takes the address of emp automatically for the call. Use a pointer receiver when the method must modify the struct, or when the struct is large and copying it would be wasteful.
Naming and visibility
Go controls visibility by the first letter of a name. A name that starts with an uppercase letter is exported, meaning other packages can use it, and a lowercase name is private to its package. That is why the lesson's title and hireMe are lowercase: everything lives in one package.
The exercise, step by step
The exercise defines a Job type with one field and one method, then uses them in mainFunction.
- Declare the struct with
type Job struct, abovemainFunction. - Give it a
titlefield of typestring. - Declare the
hireMemethod with a value receiver namedjob. It prints"You're hired as a "joined withjob.title. - In
mainFunction, createjobwith:=and aJobliteral whosetitleis"Software Engineer". - Call
job.hireMe().
The complete solution:
type Job struct {
title string
}
func (job Job) hireMe() {
fmt.Println("You're hired as a " + job.title)
}
func mainFunction() {
job := Job{title: "Software Engineer"}
job.hireMe()
}
Running it prints You're hired as a Software Engineer. A value receiver is enough here, because hireMe only reads the field.
Common mistakes
- Putting a colon between a field name and its type (
title: string), or a comma after it. Go wantstitle stringon its own line. - Forgetting the receiver when reading a field. Inside the method, write
job.title, nottitle. - Expecting a value receiver to modify the caller's struct. It works on a copy.
- Searching for a constructor keyword. Use a struct literal, or write a
New...function. - Leaving a declared variable unused. Go reports an error.
Key takeaways
- Structs hold the data, and methods are attached to the type separately, not placed inside it.
- A receiver is an explicit parameter, and it is how a method reaches its fields.
- Value receivers copy, pointer receivers modify.
- Struct literals and
New...functions do the job of constructors.