julia> struct foo{T}
x::T
end
julia> mutable struct bar{T}
x::T
end
julia> g(x::foo) = foo(3x.x)
g (generic function with 2 methods)
julia> function g(x::bar)
x.x *= 3
x
end
g (generic function with 2 methods)
julia> function f(x)
g(x)
end
f (generic function with 1 method)
julia> foo1 = foo(9.2)
foo{Float64}(9.2)
julia> bar1 = bar(9.2)
bar{Float64}(9.2)
julia> f(foo1)
foo{Float64}(27.599999999999998)
julia> f(bar1)
bar{Float64}(27.599999999999998)
julia> foo1
foo{Float64}(9.2)
julia> bar1
bar{Float64}(27.599999999999998)
julia> @code_warntype f(bar1)
Variables:
#self# <optimized out>
x::bar{Float64}
Body:
begin
$(Expr(:inbounds, false))
# meta: location REPL[28] g 2
SSAValue(0) = (Base.mul_float)((Core.getfield)(x::bar{Float64}, :x)::Float64, (Base.sitofp)(Float64, 3)::Float64)::Float64
(Core.setfield!)(x::bar{Float64}, :x, SSAValue(0))::Float64
# meta: pop location
$(Expr(:inbounds, :pop))
return x::bar{Float64}
end::bar{Float64}
julia> @code_warntype f(foo1)
Variables:
#self# <optimized out>
x::foo{Float64}
Body:
begin
return $(Expr(:new, foo{Float64}, \:((Base.mul_float)((Base.sitofp)(Float64, 3)::Float64, (Core.getfield)(x, :x)::Float64)::Float64)))
end::foo{Float64}
The function g
was inlined into f
both for the f
compiled for foo
and for the f
compiled for bar
.
In the former, the argument x
is not mutated. In the latter, it is.
You can come up with similar examples for const vs nonconst bindings.