Composite Types & Constructors

Immutable structs are efficient (can be stack-allocated / stored inline in arrays).

14.1 Immutable structs

Immutable structs are efficient (can be stack-allocated / stored inline in arrays). Prefer them.

struct Point
    x::Float64
    y::Float64
end

p = Point(1.0, 2.0)
p.x                         # 1.0
# p.x = 3.0                 # ERROR: immutable
fieldnames(Point)           # (:x, :y)
Point(1, 2)                 # converted to Float64 automatically

14.2 Mutable structs

Base.@kwdef / @kwdef (1.9+) gives keyword constructors with defaults:

mutable struct Account
    owner::String
    balance::Float64
end
acc = Account("Ann", 100.0)
acc.balance += 50
@kwdef mutable struct Config
    host::String = "localhost"
    port::Int = 8080
    debug::Bool = false
end
Config(port=9000)

14.3 Parametric structs

struct Point2D{T<:Real}
    x::T
    y::T
end
Point2D(1, 2)          # Point2D{Int64}
Point2D(1.0, 2.5)      # Point2D{Float64}
Point2D{Float32}(1, 2)

struct Pair2{A, B}
    first::A
    second::B
end

14.4 Constructors

struct Circle
    r::Float64
    function Circle(r)                    # inner constructor: enforce invariants
        r < 0 && throw(ArgumentError("radius must be non-negative"))
        new(r)
    end
end

Circle() = Circle(1.0)                    # outer constructor (convenience)
Circle(d::Dict) = Circle(d["r"])

struct Interval{T}
    lo::T
    hi::T
    Interval{T}(lo, hi) where {T} = lo <= hi ? new{T}(lo, hi) : error("lo > hi")
end
Interval(lo::T, hi::T) where {T} = Interval{T}(lo, hi)

14.5 Customizing display, equality, and copying

Base.show(io::IO, p::Point) = print(io, "Point(", p.x, ", ", p.y, ")")
Base.:(==)(a::Point, b::Point) = a.x == b.x && a.y == b.y     # default for immutables is field-wise ===
Base.hash(p::Point, h::UInt) = hash(p.x, hash(p.y, h))
Base.copy(a::Account) = Account(a.owner, a.balance)

14.6 Properties & accessors

Accessors.jl / Setfield.jl allow "updating" immutable structs: @set p.x = 5.0.

Base.getproperty(p::Point, s::Symbol) = s === :r ? hypot(getfield(p, :x), getfield(p, :y)) : getfield(p, s)
Base.propertynames(::Point) = (:x, :y, :r)