Why `let anInt: Int = Double.pi` is not allowed but `var aaa: [Int] = [1, 2, 3, 4, 5, 6, 7].reversed()` is okay?


I guess the second expresión implicitly call the suitable Array.init() to create a new array.

Why the first expression doesn’t do the implicit conversion but the second one does.

What’s the different in these two cases to have two different treatment ?

Not quite. This isn't a question of implicit conversion, but of overloading at different points in the Collection hierarchy, along with the (sometime surprising) impact of Swift's overload resolution.

Sequence has the a method Sequence.reversed() -> [Element] which returns an Array. This is the only thing you can really do with something you can't iterate backwards – you have to slurp up all the elements into a buffer, then reverse it.

BidirectionalCollection can do better. It can return a lazy wrapper, because you can wrap a BiDi collection and then serve up its elements in reverse order.

If you just write let reversed = arrrayOfInt.reversed() it'll call the most specific method, so reversed will be a ReversedCollection<[Int]>.

But if you force the return value to [Int], the solver will discard that possibility, and it falls back to a less favored option that does work, which is the version on Sequence that returns an [Int].

So there is no conversion happening here – just a different method than maybe was expected being called.



Why with this:

var aaa = [1, 2, 3, 4, 5, 6, 7].reversed()  // don’t force it to [Int]
let xxx = aaa.popLast()  // compile error here

I get error:

Referencing instance method 'popLast()' on 'BidirectionalCollection' requires the types 'ReversedCollection<[Int]>' and 'ReversedCollection<[Int]>.SubSequence' (aka 'Slice<ReversedCollection<Array>>') be equivalent

How can I overcome this without forcing it to be a [Int], incurring copying? How can I operate on ReversedCollection<[Int]>?

Create a subsequence of the reversed collection:

    let reversed = [1, 2, 3, 4, 5, 6, 7].reversed()
    var slice = reversed[...]
    let element = slice.popLast()

Subsequences can have their last element popped easily because it's just a question of reducing the bounds of the slice, not actually mutating the underlying base collection.


(the rather enigmatic error message is telling you that you can't pop the end off an arbitrary collection, but you can pop the end of a collection who's subsequence is Self)

1 Like

I've become used to this workaround, from running into this problem frequently enough, but I still wish it wasn't necessary. It does feel very hacky to do things like [...] when you don't explicitly care about the types involved, you just want something to work, like popLast.

I don't have any specific ideas for how to improve that, though. :pensive: