Go methods become much easier once you understand one idea:
A method is simply a function attached to a type.
And the important rule is:
A receiver can be based on almost any type you define in the same package — not only a
struct.
You can create methods on your own defined types based on:
struct
int
string
float
slice
map
array
function
channel
The struct case is simply the most common one.
1. What is a Method in Go?
A normal function looks like this:
func greet(name string) {
fmt.Println("Hello", name)
}
You call it like:
greet("Rajesh")
A method has one extra part called the receiver:
func (u User) Greet() {
fmt.Println("Hello", u.Name)
}
The receiver is:
(u User)
So:
func (receiver) MethodName(parameters) return-types
Example:
func (u User) Greet() {
fmt.Println("Hello", u.Name)
}
You call the method through the value:
u.Greet()
instead of:
Greet(u)
2. Why Do We Need Methods?
Suppose we have:
type User struct {
Name string
}
Without a method:
func greet(u User) {
fmt.Println("Hello", u.Name)
}
Call:
greet(user)
With a method:
func (u User) Greet() {
fmt.Println("Hello", u.Name)
}
Call:
user.Greet()
The method version is usually easier to understand because the behavior belongs naturally to User.
Think:
User
├── Name
├── Greet()
├── Login()
├── Logout()
└── ChangePassword()
Instead of having unrelated-looking functions:
greet(user)
login(user)
logout(user)
changePassword(user)
3. Function vs Method
| Feature | Function | Method |
|---|---|---|
| Attached to type | ❌ | ✅ |
| Has receiver | ❌ | ✅ |
Called using . | Usually no | ✅ |
| Example | add(10,20) | user.Greet() |
| Best for | General operations | Behavior belonging to a type |
Function:
func Add(a, b int) int {
return a + b
}
Call:
Add(10, 20)
Method:
func (c Calculator) Add(a, b int) int {
return a + b
}
Call:
calculator.Add(10, 20)
4. Basic Method Example
package main
import "fmt"
// User is our own defined type.
type User struct {
Name string
}
// Greet is a method attached to User.
//
// u = receiver variable
// User = receiver type
func (u User) Greet() {
fmt.Println("Hello", u.Name)
}
func main() {
// Create a User value.
user := User{
Name: "Rajesh",
}
// Call the method.
user.Greet()
}
Output:
Hello Rajesh
The important line is:
func (u User) Greet()
Break it down:
func
│
│ receiver
│ ┌──────┐
func (u User) Greet()
│ │ │
│ │ └── method name
│
└── receiver variable
5. What Is a Receiver?
The receiver tells Go:
“This function belongs to this type.”
Example:
func (u User) Greet() {
}
Here:
u
is the receiver variable.
And:
User
is the receiver type.
Inside the method, u gives you access to that particular User.
Example:
func (u User) Greet() {
fmt.Println(u.Name)
}
If:
user := User{Name: "Rajesh"}
and you call:
user.Greet()
then inside the method:
u.Name
is:
Rajesh
6. The Most Important Receiver Rule
A simplified rule that is excellent for learning is:
You can define methods on your own defined types in the same package.
The underlying type does not have to be a struct.
For example:
type Age int
type Name string
type Numbers []int
type Scores map[string]int
type Coordinates [2]int
type Task func()
type MessageChannel chan string
All of these are types you defined.
Therefore, methods can be attached to them.
7. Struct Receiver
This is by far the most common case.
package main
import "fmt"
type User struct {
Name string
}
func (u User) Greet() {
fmt.Println("Hello", u.Name)
}
func main() {
u := User{
Name: "Rajesh",
}
u.Greet()
}
Output:
Hello Rajesh
When should you use it?
Struct methods are excellent when modeling things such as:
User
Employee
Customer
Order
Product
Server
Database
Car
BankAccount
For example:
type BankAccount struct {
Balance float64
}
Possible methods:
Deposit()
Withdraw()
Balance()
8. Custom int Receiver
Methods can also belong to your own integer type.
package main
import "fmt"
// Age is a new defined type
// whose underlying type is int.
type Age int
// IsAdult is a method on Age.
func (a Age) IsAdult() bool {
return a >= 18
}
func main() {
var age Age = 25
fmt.Println(age.IsAdult())
}
Output:
true
Notice:
type Age int
This does not mean Age is merely a variable.
It creates a new defined type.
Then:
func (a Age) IsAdult() bool
attaches behavior to it.
This can make code very readable:
age.IsAdult()
9. Custom string Receiver
You can create methods on your own string type.
package main
import (
"fmt"
"strings"
)
type Name string
func (n Name) Upper() string {
return strings.ToUpper(string(n))
}
func main() {
name := Name("rajesh")
fmt.Println(name.Upper())
}
Output:
RAJESH
Notice this:
string(n)
Because Name is our own type:
type Name string
we convert it back to string when calling a standard library function expecting a string.
10. Slice Receiver
A slice can also be the underlying type of your own defined type.
package main
import "fmt"
type Numbers []int
func (n Numbers) Sum() int {
total := 0
for _, value := range n {
total += value
}
return total
}
func main() {
nums := Numbers{10, 20, 30}
fmt.Println(nums.Sum())
}
Output:
60
This is quite useful.
Instead of writing:
Sum(nums)
you can write:
nums.Sum()
It reads naturally:
Numbers, calculate your sum.
11. Map Receiver
Maps can also have methods when you create your own defined map type.
package main
import "fmt"
type Scores map[string]int
func (s Scores) Get(name string) int {
return s[name]
}
func main() {
scores := Scores{
"Rajesh": 90,
"Amit": 80,
}
fmt.Println(scores.Get("Rajesh"))
}
Output:
90
You could also create methods such as:
Add()
Remove()
Exists()
Average()
Highest()
Example conceptually:
scores.Get("Rajesh")
scores.Add("John", 95)
scores.Remove("Amit")
12. Array Receiver
Arrays can also be used.
package main
import "fmt"
type Coordinates [2]int
func (c Coordinates) Print() {
fmt.Println(c[0], c[1])
}
func main() {
c := Coordinates{10, 20}
c.Print()
}
Output:
10 20
Here:
type Coordinates [2]int
creates a new defined type based on an array.
Then:
func (c Coordinates) Print()
adds behavior to it.
13. Function Receiver
This is surprising when first learning Go.
Even a function can be the underlying type of a type with methods.
package main
import "fmt"
type Task func()
func (t Task) Run() {
fmt.Println("Starting task...")
t()
}
func main() {
task := Task(func() {
fmt.Println("Task running")
})
task.Run()
}
Output:
Starting task...
Task running
Here:
type Task func()
defines a function type.
Then:
func (t Task) Run()
adds a method to it.
And because t itself is a function, this runs it:
t()
14. Channel Receiver
Channels can also be used.
package main
import "fmt"
type MessageChannel chan string
func (m MessageChannel) Send(message string) {
m <- message
}
func main() {
ch := make(MessageChannel, 1)
ch.Send("Hello")
fmt.Println(<-ch)
}
Output:
Hello
Instead of writing:
ch <- "Hello"
you created behavior:
ch.Send("Hello")
This can sometimes make APIs easier to understand.
15. Float Receiver
The same idea applies to floating-point types.
package main
import "fmt"
type Price float64
func (p Price) WithTax() Price {
return p * 1.18
}
func main() {
price := Price(100)
fmt.Println(price.WithTax())
}
Output:
118
Or approximately that value depending on formatting.
This gives you expressive code:
price.WithTax()
16. You Cannot Attach a Method Directly to int
This is invalid:
func (x int) Double() int {
return x * 2
}
Go will reject it.
Why?
Because:
int
is a built-in type defined by Go.
You did not define it in your package.
Instead, define your own type:
type MyInt int
Then:
func (x MyInt) Double() MyInt {
return x * 2
}
Complete example:
package main
import "fmt"
type MyInt int
func (x MyInt) Double() MyInt {
return x * 2
}
func main() {
number := MyInt(10)
fmt.Println(number.Double())
}
Output:
20
17. Same Package Rule
Suppose package people defines:
package people
type User struct {
Name string
}
From another package you cannot do this:
package main
import "example.com/people"
func (u people.User) Greet() {
}
❌ Not allowed.
Why?
Because User belongs to package people.
Methods for User must be declared in the package where the receiver base type is defined.
So conceptually:
package people
type User struct{}
func (u User) Greet() {}
✅ Correct.
18. Why Does Go Have This Rule?
It keeps ownership clear.
Imagine package A creates:
User
and package B could secretly add:
User.Delete()
and package C adds:
User.Save()
Then it would become difficult to know what methods actually belong to User.
Go avoids this.
The package defining the type controls its methods.
19. Value Receiver
So far, we have mostly written:
func (u User) Greet()
This is called a value receiver.
Example:
package main
import "fmt"
type User struct {
Name string
}
func (u User) ChangeName() {
u.Name = "Amit"
fmt.Println("Inside:", u.Name)
}
func main() {
user := User{
Name: "Rajesh",
}
user.ChangeName()
fmt.Println("Outside:", user.Name)
}
Output:
Inside: Amit
Outside: Rajesh
Why?
Because:
(u User)
receives a copy of the User value.
Conceptually:
Original User
|
| copy
v
Method receiver
Changing the copy does not change the original struct.
20. Pointer Receiver
If you want the method to modify the original value, you commonly use a pointer receiver:
func (u *User) ChangeName()
Example:
package main
import "fmt"
type User struct {
Name string
}
func (u *User) ChangeName() {
u.Name = "Amit"
}
func main() {
user := User{
Name: "Rajesh",
}
user.ChangeName()
fmt.Println(user.Name)
}
Output:
Amit
Now the original struct was modified.
21. Value Receiver vs Pointer Receiver
This is one of the most important method concepts in Go.
Value receiver:
func (u User) Method()
Pointer receiver:
func (u *User) Method()
Main difference:
| Receiver | Gets | Can modify original? | Typical use |
|---|---|---|---|
User | Copy | Usually ❌ | Read-only behavior |
*User | Pointer | ✅ | Modify the value |
Easy memory rule:
(User)
↓
copy
(*User)
↓
original
22. Complete Value vs Pointer Example
package main
import "fmt"
type Counter struct {
Value int
}
// Value receiver gets a copy.
func (c Counter) Show() {
fmt.Println("Current:", c.Value)
}
// Pointer receiver can change original.
func (c *Counter) Increment() {
c.Value++
}
func main() {
counter := Counter{
Value: 10,
}
counter.Show()
counter.Increment()
counter.Show()
}
Output:
Current: 10
Current: 11
23. Why Don’t We Write &counter?
You may notice:
counter.Increment()
works even though:
Increment()
expects:
*Counter
Go automatically handles this in many ordinary method calls.
You could think of:
counter.Increment()
roughly as:
(&counter).Increment()
when counter is addressable.
This is one reason Go pointer receiver syntax stays relatively clean.
24. Methods Can Have Parameters
A receiver is not a replacement for parameters.
You can have both.
Example:
package main
import "fmt"
type Calculator struct{}
func (c Calculator) Add(a int, b int) int {
return a + b
}
func main() {
calc := Calculator{}
result := calc.Add(10, 20)
fmt.Println(result)
}
Output:
30
Here:
(c Calculator)
is the receiver.
And:
a int, b int
are normal function parameters.
25. Methods Can Return Values
Exactly like functions.
package main
import "fmt"
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func main() {
r := Rectangle{
Width: 10,
Height: 5,
}
fmt.Println(r.Area())
}
Output:
50
26. Methods Can Return Multiple Values
Methods have the same return capabilities as normal functions.
package main
import "fmt"
type Calculator struct{}
func (c Calculator) Divide(a, b float64) (float64, bool) {
if b == 0 {
return 0, false
}
return a / b, true
}
func main() {
calc := Calculator{}
result, ok := calc.Divide(10, 2)
fmt.Println(result)
fmt.Println(ok)
}
Output:
5
true
27. One Type Can Have Many Methods
This is where methods become especially useful.
package main
import "fmt"
type BankAccount struct {
Owner string
Balance float64
}
func (b BankAccount) ShowBalance() {
fmt.Println("Balance:", b.Balance)
}
func (b *BankAccount) Deposit(amount float64) {
b.Balance += amount
}
func (b *BankAccount) Withdraw(amount float64) {
b.Balance -= amount
}
func main() {
account := BankAccount{
Owner: "Rajesh",
Balance: 1000,
}
account.ShowBalance()
account.Deposit(500)
account.Withdraw(200)
account.ShowBalance()
}
Output:
Balance: 1000
Balance: 1300
Now the type feels like one logical object:
BankAccount
│
├── Owner
├── Balance
│
├── ShowBalance()
├── Deposit()
└── Withdraw()
28. When Should I Create a Method?
Create a method when the operation logically belongs to the type.
For example:
type User struct{}
Good candidates:
user.Login()
user.Logout()
user.FullName()
user.IsAdmin()
Because all these describe behavior of a User.
For:
type Order struct{}
Methods might be:
order.Total()
order.Cancel()
order.Pay()
order.Ship()
For:
type Numbers []int
methods might be:
numbers.Sum()
numbers.Average()
numbers.Max()
29. When Should I Use a Normal Function Instead?
Not every function needs to be a method.
Suppose:
func Add(a, b int) int
There may be no meaningful object that owns the operation.
Then a normal function is perfectly appropriate.
Use a method when:
"This operation belongs to this type."
Use a function when:
"This is a general operation."
Example:
user.Save()
makes sense.
But:
Math.Add()
may be unnecessary in Go when:
Add(a, b)
is simpler.
30. Where Are Methods Declared?
Methods are usually declared in the same package as their receiver type.
You might have:
project/
│
├── go.mod
│
├── main.go
│
└── user/
├── user.go
└── methods.go
For example:
user/user.go
package user
type User struct {
Name string
}
user/methods.go
package user
import "fmt"
func (u User) Greet() {
fmt.Println("Hello", u.Name)
}
These methods don’t have to physically be in the same .go file.
They just need to belong to the same package as the receiver’s defined type.
That distinction is important:
Same package does not mean same file.
31. Receiver Name Can Be Anything
This:
func (u User) Greet()
could technically be:
func (x User) Greet()
or:
func (user User) Greet()
But Go commonly uses short receiver names.
For example:
func (u User)
func (c Customer)
func (o Order)
func (p Product)
func (s Server)
This is common Go style.
32. Avoid this and self
Coming from Java, C++, Python, etc., you may expect:
this
or:
self
Go does not have a special this or self keyword.
Instead you explicitly choose the receiver name:
func (u User) Greet() {
fmt.Println(u.Name)
}
So:
u
plays the role that this or self often plays in other languages.
33. Methods Are Important for Interfaces
This is a major reason methods matter in Go.
Suppose we define an interface:
type Speaker interface {
Speak()
}
And a type:
type Dog struct{}
with:
func (d Dog) Speak() {
fmt.Println("Woof")
}
Then Dog automatically satisfies Speaker.
Complete example:
package main
import "fmt"
type Speaker interface {
Speak()
}
type Dog struct{}
func (d Dog) Speak() {
fmt.Println("Woof")
}
func makeSound(s Speaker) {
s.Speak()
}
func main() {
dog := Dog{}
makeSound(dog)
}
Output:
Woof
Notice there is no:
implements Speaker
statement.
Go checks the methods.
Because Dog has:
Speak()
it satisfies:
Speaker
34. Methods Create the Method Set
Every type has a set of methods associated with it.
Suppose:
type User struct{}
and:
func (u User) Greet() {}
func (u User) Name() {}
func (u User) IsAdmin() {}
Then conceptually:
User Method Set
│
├── Greet()
├── Name()
└── IsAdmin()
Interfaces check these methods to determine whether the type satisfies them.
35. Pointer Receivers and Interfaces
This is a slightly more advanced but very important topic.
Consider:
type Speaker interface {
Speak()
}
type Dog struct{}
func (d *Dog) Speak() {
fmt.Println("Woof")
}
Because Speak() has a pointer receiver:
*Dog
implements Speaker.
But the plain Dog value does not have that pointer-receiver method in its method set for interface satisfaction.
So this works:
dog := &Dog{}
makeSound(dog)
But this does not:
dog := Dog{}
makeSound(dog)
when Speak() exists only on *Dog.
This distinction matters especially with interfaces.
36. One Important Technical Precision
Earlier we used the simple rule:
“A receiver can be almost any named type you define.”
The precise Go rule is slightly more specific.
The receiver base type must be a defined type belonging to the same package, and the base type cannot be an interface or pointer type.
For normal learning, remember:
You define a type
↓
in your package
↓
attach methods to it
Examples:
type Age int
type Name string
type User struct{}
type Numbers []int
Perfectly valid.
37. Defined Type vs Alias — Important
Consider:
type Age int
This creates a new defined type.
Therefore:
func (a Age) IsAdult() bool
is valid.
But this:
type Age = int
is a type alias.
It means:
Age is simply another spelling for int.
It does not create a new local defined type that lets you attach methods to built-in int.
So understand the difference:
type Age int
New defined type.
Versus:
type Age = int
Alias.
For methods, you usually want:
type Age int
38. Complete Example: Multiple Receiver Types
Here is one program demonstrating several types.
package main
import (
"fmt"
"strings"
)
// ---------- Struct ----------
type User struct {
Name string
}
func (u User) Greet() {
fmt.Println("Hello", u.Name)
}
// ---------- Integer ----------
type Age int
func (a Age) IsAdult() bool {
return a >= 18
}
// ---------- String ----------
type Name string
func (n Name) Upper() string {
return strings.ToUpper(string(n))
}
// ---------- Slice ----------
type Numbers []int
func (n Numbers) Sum() int {
total := 0
for _, number := range n {
total += number
}
return total
}
// ---------- Map ----------
type Scores map[string]int
func (s Scores) Get(name string) int {
return s[name]
}
// ---------- Array ----------
type Coordinates [2]int
func (c Coordinates) Print() {
fmt.Println(c[0], c[1])
}
// ---------- Function ----------
type Task func()
func (t Task) Run() {
fmt.Println("Starting task...")
t()
}
// ---------- Channel ----------
type MessageChannel chan string
func (m MessageChannel) Send(message string) {
m <- message
}
func main() {
// Struct
user := User{Name: "Rajesh"}
user.Greet()
// Integer
age := Age(25)
fmt.Println("Adult:", age.IsAdult())
// String
name := Name("rajesh")
fmt.Println("Upper:", name.Upper())
// Slice
numbers := Numbers{10, 20, 30}
fmt.Println("Sum:", numbers.Sum())
// Map
scores := Scores{
"Rajesh": 90,
}
fmt.Println("Score:", scores.Get("Rajesh"))
// Array
coordinates := Coordinates{10, 20}
coordinates.Print()
// Function
task := Task(func() {
fmt.Println("Task running")
})
task.Run()
// Channel
channel := make(MessageChannel, 1)
channel.Send("Hello from channel")
fmt.Println(<-channel)
}
Expected output:
Hello Rajesh
Adult: true
Upper: RAJESH
Sum: 60
Score: 90
10 20
Starting task...
Task running
Hello from channel
39. Receiver Types Summary
| Underlying type | Example | Method allowed? |
|---|---|---|
| Struct | type User struct{} | ✅ |
| Integer | type Age int | ✅ |
| String | type Name string | ✅ |
| Float | type Price float64 | ✅ |
| Slice | type Numbers []int | ✅ |
| Map | type Scores map[string]int | ✅ |
| Array | type Point [2]int | ✅ |
| Function | type Task func() | ✅ |
| Channel | type Messages chan string | ✅ |
Built-in int directly | int | ❌ |
Built-in string directly | string | ❌ |
| Type from another package | http.Client | ❌ |
40. Method Anatomy
Take:
func (u *User) ChangeName(name string) bool {
u.Name = name
return true
}
Breakdown:
func
│
│ receiver
│ ┌───────┐
│ │
func (u *User) ChangeName(name string) bool {
│ │ │ │ │
│ │ │ │ └── return type
│ │ │ │
│ │ │ └── parameter
│ │ │
│ │ └── method name
│ │
│ └── receiver type
│
└── receiver variable
So a method contains:
Receiver
Method name
Parameters
Return values
Body
Almost exactly like a function, except for the receiver.
41. How to Design a Method
When creating a type, ask:
What actions naturally belong to this value?
For:
type Employee struct {
Name string
Salary float64
}
Potential methods:
employee.FullName()
employee.GiveRaise()
employee.CalculateTax()
employee.PrintDetails()
You probably wouldn’t create:
employee.ConnectDatabase()
unless database connection genuinely belongs to the employee concept.
Method design should represent logical behavior.
42. Real-World Example
package main
import "fmt"
type BankAccount struct {
AccountHolder string
Balance float64
}
func (a BankAccount) ShowBalance() {
fmt.Println("Balance:", a.Balance)
}
func (a *BankAccount) Deposit(amount float64) {
if amount <= 0 {
fmt.Println("Invalid deposit")
return
}
a.Balance += amount
}
func (a *BankAccount) Withdraw(amount float64) {
if amount > a.Balance {
fmt.Println("Insufficient balance")
return
}
a.Balance -= amount
}
func main() {
account := BankAccount{
AccountHolder: "Rajesh",
Balance: 1000,
}
account.ShowBalance()
account.Deposit(500)
account.ShowBalance()
account.Withdraw(300)
account.ShowBalance()
}
Output:
Balance: 1000
Balance: 1500
Balance: 1200
This is a natural place for methods because:
BankAccount
│
├── data
│ ├── AccountHolder
│ └── Balance
│
└── behavior
├── ShowBalance()
├── Deposit()
└── Withdraw()
43. How Method Calls Work Mentally
Given:
account.Deposit(500)
Read it naturally as:
“Ask this account to deposit 500.”
Given:
numbers.Sum()
read:
“Ask these numbers for their sum.”
Given:
age.IsAdult()
read:
“Ask this age whether it represents an adult.”
This mental model makes method APIs much easier to design.
44. Method vs Struct vs Interface
These three concepts are closely connected.
Struct
Stores data.
type Dog struct {
Name string
}
Method
Adds behavior.
func (d Dog) Speak() {
fmt.Println("Woof")
}
Interface
Defines required behavior.
type Speaker interface {
Speak()
}
Together:
STRUCT
Dog{Name}
│
│ has
▼
METHOD
Speak()
│
│ satisfies
▼
INTERFACE
Speaker
This relationship is central to Go.
45. The Big Picture
Suppose:
type Dog struct {
Name string
}
Data:
Name
Method:
func (d Dog) Speak()
Now Dog has behavior:
Dog
├── Name
└── Speak()
Then:
type Speaker interface {
Speak()
}
Because Dog has Speak():
Dog satisfies Speaker
You do not explicitly declare that relationship.
Go discovers it from the methods.
46. Best Practice: Value or Pointer Receiver?
A useful beginner rule:
Use a pointer receiver when the method changes the value.
func (u *User) ChangeName(name string)
Use a value receiver when it only reads the value and the type is reasonably small.
func (u User) FullName() string
For larger structs, pointer receivers are also often preferred to avoid copying the entire struct.
Example:
type User struct {
Name string
Email string
}
Read-only:
func (u User) DisplayName() string {
return u.Name
}
Modification:
func (u *User) ChangeEmail(email string) {
u.Email = email
}
47. One Style Recommendation
If a type has several methods and some need pointer receivers, it is often cleaner to use pointer receivers consistently for that type.
For example:
func (u *User) ChangeName() {}
func (u *User) Save() {}
func (u *User) Validate() {}
rather than randomly mixing receiver styles without reason.
This becomes especially important when interfaces and method sets are involved.
48. Common Beginner Mistakes
Mistake 1: Thinking methods require structs
Wrong:
Method = struct function
Better:
Method = function attached to your own defined type
Structs are just the most common receiver types.
Mistake 2: Trying to attach a method to int
Wrong:
func (x int) Double() int
Correct:
type MyInt int
func (x MyInt) Double() MyInt
Mistake 3: Expecting value receiver changes to persist
This:
func (u User) ChangeName()
works on a copy.
For modification, usually use:
func (u *User) ChangeName()
Mistake 4: Thinking receiver is a normal parameter
This:
func (u User) Greet(name string)
contains:
u → receiver
name → parameter
They serve different roles.
Mistake 5: Thinking method must be in same file
No.
It must belong to the appropriate package, not necessarily the same source file.
49. Quick Decision Guide
Ask these questions:
| Question | Choice |
|---|---|
| Does behavior belong to a type? | Method |
| Is it a general operation? | Function |
| Need to change original value? | Pointer receiver |
| Only reading small value? | Value receiver often fine |
| Want type to satisfy interface? | Implement required methods |
Want method on int/string? | First define your own type |
| Type belongs to another package? | Cannot add methods directly |
50. Final Mental Model
Remember this:
TYPE
│
│ define data/value
│
▼
METHOD
│
│ adds behavior
│
▼
INTERFACE
│
│ describes required behavior
│
▼
POLYMORPHISM
And the core syntax is simply:
func (receiver ReceiverType) MethodName() {
}
Example:
func (u User) Greet() {
}
Or with a pointer:
func (u *User) ChangeName() {
}
The most important lesson is:
A Go method is a function attached to a locally defined type through a receiver. The receiver is not limited to structs. Your own types based on integers, strings, floats, slices, maps, arrays, functions, and channels can also have methods.
For everyday Go, however, you will see methods most frequently on:
type Something struct {
...
}
because structs are how Go commonly groups related data, and methods naturally provide the behavior associated with that data.