A DataFrame is (more or less) just a list of named vectors, and hcat(..., copycols=false) doesn’t copy the underlying vectors:
julia> df1, df2 = DataFrame(a = rand(10^6)), DataFrame(b = 1:10^6);
julia> @time df1 = hcat(df1, df2, copycols=false);
0.000019 seconds (18 allocations: 1.406 KiB)