Unusual empty `UnitRange`s while reinterpreting a vector

julia> r = reinterpret(UnitRange{Int}, [1,-1])[1]
1:-1

julia> isempty(r)
true

julia> length(r) # source of errors
-1

julia> 1:-1 # the constructor changes the endpoint to ensure that the length is zero
1:0

julia> r == 1:0
true

julia> collect(r) # doesn't work, as the length is negative
ERROR: ArgumentError: invalid Array dimensions

I know why this is happening, but I wonder if there’s a way around this?

Can you use this fact?

julia> r == 1:0
true

julia> r === 1:0
false

Not really in general. The issue downstream in this example is that

isempty(a::AbstractArray) = (length(a) == 0)

which requires the length to be zero. Perhaps this may be changed to length(a) <= 0?

It seems to me that by using reinterpret in this way, you are making a corrupted instance of UnitRange (corrupted in the sense that it violates an invariant that is guaranteed by the inner constructor).

You found one issue with the broken invariant. Even if you work around this, who knows which other parts might break, maybe silently…

Do you have a very very good reason to do this? Otherwise I think it’s a bad idea to try and patch this in your code, and a worse idea to patch it in the standard library.

5 Likes

reinterpret should be used with care. It literally places the bits of one object into the bits of another and nothing more. Note that 1:-1 is not a well-formed UnitRange and would normally be impossible to create (if you attempt to create 1:-1 it returns 1:0) and only exists because you bypassed the constructor via reinterpret. This is why you’re finding broken functionality.

I’d suggest you splat your two-element vector into UnitRange like this, if you must make this sort of conversion:

julia> UnitRange([1,-1]...)
1:0
2 Likes