Structs
Prerequisites: Multiple dispatch
A struct defines a new composite type — a named group of fields:
struct Point
x::Float64
y::Float64
end
p = Point(3, 4)
println(p)
println(p.x, " ", p.y)
println(fieldnames(Point))
println(typeof(p), " ", p isa Point)
distance(a::Point, b::Point) = sqrt((a.x - b.x)^2 + (a.y - b.y)^2)
println(distance(Point(0, 0), p))
Point(3.0, 4.0)
3.0 4.0
(:x, :y)
Point true
5.0
- Julia creates a constructor
Point(x, y)automatically and converts the arguments to the field types (3became3.0). - Methods live outside the struct. There are no classes: data is in structs, behavior is in functions that dispatch on them.
Immutable by default
struct Point
x::Float64
y::Float64
end
p = Point(1, 2)
try
p.x = 10
catch err
println(sprint(showerror, err))
end
moved = Point(p.x + 1, p.y)
println(moved)
println(Point(1, 2) == Point(1, 2))
setfield!: immutable struct of type Point cannot be changed
Point(2.0, 2.0)
true
Immutable structs are safe to share and very fast — small ones are stored inline in arrays, just like numbers. Two immutable values with equal fields are equal.
mutable struct
mutable struct Account
owner::String
balance::Float64
end
function deposit!(acc::Account, amount)
amount > 0 || throw(ArgumentError("amount must be positive"))
acc.balance += amount
return acc
end
acc = Account("Ada", 100)
deposit!(acc, 50)
println(acc.balance)
println(Account("Ada", 1) == Account("Ada", 1))
150.0
false
Use mutable struct when an object's state genuinely changes over time.
Mutable objects have identity: two separate accounts with the same
fields aren't == unless you define == for them.
Custom printing
struct Temperature
celsius::Float64
end
Base.show(io::IO, t::Temperature) = print(io, round(t.celsius, digits = 1), " °C")
t = Temperature(21.456)
println(t)
println([Temperature(0), Temperature(100)])
println("It's $t outside")
21.5 °C
Temperature[0.0 °C, 100.0 °C]
It's 21.5 °C outside
Defining Base.show(io::IO, x::YourType) controls how your type prints
everywhere — in println, in arrays, in interpolation, and in the REPL.
Redefining structs
Before Julia 1.12, redefining a struct with different fields in a running session was an error and you had to restart Julia. Since 1.12 it's allowed: new code uses the new definition, while objects created earlier keep the old type. Revise.jl tracks such changes in your files automatically.
Practice
Define a Rectangle struct with width and height, functions
area and perimeter, and a show method that prints Rectangle 3×4.
Show solution
struct Rectangle
width::Float64
height::Float64
end
area(r::Rectangle) = r.width * r.height
perimeter(r::Rectangle) = 2 * (r.width + r.height)
Base.show(io::IO, r::Rectangle) = print(io, "Rectangle ", r.width, "×", r.height)
r = Rectangle(3, 4)
println(r, ": area ", area(r), ", perimeter ", perimeter(r))
Rectangle 3.0×4.0: area 12.0, perimeter 14.0