Assignment to multiple targets - how does it work?




julia> a = zeros(5); b = zeros(5); R = collect(10:10:100);

This naturally fails:

julia> a[1:end] = R
ERROR: DimensionMismatch("tried to assign 10 elements to 5 destinations")
 [1] throw_setindex_mismatch(::Array{Float64,1}, ::Tuple{Int64}) at .\indices.jl:92
 [2] setindex_shape_check(::Array{Float64,1}, ::Int64) at .\indices.jl:143
 [3] setindex!(::Array{Float64,1}, ::Array{Float64,1}, ::UnitRange{Int64}) at .\array.jl:618

But then this succeeds:

julia> c, d = R;

julia> c

julia> d

Why doesn’t this fail with a “tried to assign 10 elements to 2 destinations” error? Why does Julia silently discard the rest of the values in such cases?


julia> a[1:end],b = R;

julia> (a,)
([10.0, 10.0, 10.0, 10.0, 10.0],)

julia> (b,)

This is seen as a broadcast of R’s first value into a (and gives an appropriate warning in 0.7), whereas:

julia> m, n, o, p, q, b = R;

julia> (m, n, o, p, q)
(10, 20, 30, 40, 50)

julia> (b,)

distributes R’s values over the variables.

What will be the semantics of the previous a[1:end],b = R; assignment after the deprecation is removed?

And is there an easy way to get the effect of a[1], a[2], a[3], a[4], a[5], b = R; with more compact and more generalizable code?


There are two things going on here.

First, multiple return values are destructured. Whenever you have a comma-separated list of variable names on the left hand side of a =, that is treated as a tuple and the iterable on the right hand side is assigned elementwise. Extra values are not.

The other expression is an invocation of setindex!, eg

julia> Meta.lower(Base, :(a[1:end] = R))
:($(Expr(:thunk, CodeInfo(
 1 ─ %1 = Base.lastindex(:a)::Any                                                                                                                                            │
 │   %2 = :(:)(1, %1)::Any                                                                                                                                                   │
 │        Base.setindex!(:a, :R, %2)                                                                                                                                         │
 └──      return :R                                                                                                                                                          │

setindex! checks for conforming sizes, hence the DimensionMismatch.