Operations on numbers (like UInt32) are never “in place” (since they’re immutable) so the function wouldn’t need the !. The reason for the line a::UInt32 is that, in that context, the type annotation is a “guarantee” that a will always be a UInt32, no matter what happens to it. In practice, this means that every a = a... operation is followed by a convert(UInt32, a).
See:
julia> function foo(x::Int)
y::Int = x
y += 1.0
y
end
foo (generic function with 1 method)
julia> function bar(x::Int)
y = x
y += 1.0
y
end
bar (generic function with 1 method)
julia> foo(1)
2
julia> bar(1)
2.0
edit with another example, with a number that cannot be converted back to Int
julia> function baz(x)
y::Int = x
y += 1.1
y
end
baz (generic function with 1 method)
julia> baz(1)
ERROR: InexactError: Int64(2.1)
Stacktrace:
[1] Int64 at ./float.jl:710 [inlined]
[2] convert at ./number.jl:7 [inlined]
[3] baz(::Int64) at ./REPL[39]:3
[4] top-level scope at REPL[40]:1