Methods
A method is a function with one extra ingredient: a receiver, declared in
its own parentheses between func and the method name. The receiver binds the
function to a type, so you call it with dot notation:
package main
import (
"fmt"
"math"
)
type Point struct {
X, Y float64
}
// Distance is a method: p is the receiver.
func (p Point) Distance(q Point) float64 {
return math.Hypot(q.X-p.X, q.Y-p.Y)
}
func main() {
a := Point{0, 0}
b := Point{3, 4}
fmt.Println(a.Distance(b)) // 5
}
p behaves exactly like a parameter — it is one, just written before the
name. There's no this or self keyword; you pick the receiver's name, and by
convention it's short: p for a Point, c for a Client.
Not just structs
Here's where Go differs from class-based languages: you can declare methods on
any named type you define, not only structs. A type built on float64, a
slice, a map, even a function type — all can carry methods:
type Temperature float64
func (t Temperature) Freezing() bool {
return t <= 0
}
type Path []Point
func (p Path) Length() float64 {
sum := 0.0
for i := 1; i < len(p); i++ {
sum += p[i-1].Distance(p[i])
}
return sum
}
Temperature is "just" a float64 underneath, but naming it lets you hang
domain behavior on it — t.Freezing() reads better than a free function, and
the type itself documents what the number means. The only rule: the method
must be declared in the same package as the type, so you can't add methods to
int or to someone else's types directly.
Methods are declared per type, found by the compiler
A method set belongs to its type. Two types can each have a Length method
with completely different implementations, and the compiler picks the right
one from the receiver's type at the call site — no inheritance hierarchy, no
overriding, no surprises about which version runs.
This is Go's philosophy in miniature: instead of building taxonomies of classes, you define small concrete types and give each exactly the behavior it needs. Later, interfaces will let types with the same method signatures be used interchangeably — but the types themselves stay independent.
Method values, in passing
One more thing to file away: a.Distance without the call parentheses is a
method value — a regular function with the receiver already bound — which
you can assign to a variable or pass around like any other function.
Try it
The exercise asks you to declare a method on a named float64 type —
converting Celsius to Fahrenheit — proving methods need no struct at all.
Practice
1. Celsius to Fahrenheit
IntermediateThe starter code defines two named temperature types:
type Celsius float64 type Fahrenheit float64Implement
ToFahrenheitas a method onCelsius:func (c Celsius) ToFahrenheit() FahrenheitIt converts the temperature using the classic formula F = C × 9⁄5 + 32, so:
Celsius(0).ToFahrenheit()returns32Celsius(100).ToFahrenheit()returns212Celsius(-40).ToFahrenheit()returns-40
Note that both types are named
float64s — no struct in sight. Remember that the method must return aFahrenheit, so convert the computed value withFahrenheit(...).package main import "fmt" // Celsius and Fahrenheit are named float64 types — methods can attach to // any named type, not just structs. type Celsius float64 type Fahrenheit float64 // ToFahrenheit converts the temperature to Fahrenheit (F = C*9/5 + 32). func (c Celsius) ToFahrenheit() Fahrenheit { // TODO: implement return 0 } func main() { boiling := Celsius(100) fmt.Println(boiling.ToFahrenheit()) }Sign in to write and run your solution in the browser.
Sign in for hints and the solution.