0

I have a Swift project in which I use 2 extensions on any Collection type. They allow for safe access to elements in an array. Here's the extensions:

subscript (safe index: Index) -> Element? {
    return indices.contains(index) ? self[index] : nil
}

subscript (safe int: Int) -> Element? {
    let index = self.index(self.startIndex, offsetBy: int)
    return self[index]
}

This works great, but in Xcode 10.2 there are now compiler errors.

I tried to be as explicit as possible about the type in the subscript and the type of the element, but it didn't work.

Does anyone know why this doesn't work anymore?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
invalidArgument
  • 2,289
  • 4
  • 24
  • 35

1 Answers1

1

I cannot explain why this did work in former versions of Xcode, but don't you think your two subscript(safe:) is ambiguous enough when Collection.Index == Int?

And, with your second subscript(safe:), the word safe does not seem to be appropriate as it may easily crash your app when int is out of bounds of the collection.

extension Collection {
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }

    subscript (safe int: Int) -> Element? {
        let index = self.index(self.startIndex, offsetBy: int)
        return self[index]
    }
}
var arr: [String] = ["a"]
print(arr[safe: 1])

Causes Thread 1: Fatal error: Index out of range on the line return self[index]. (Tested with Xcode 10.1.)

You should better use another word than safe for your second subscript, and the two subscript may never be ambiguous in any versions of Xcode.

extension Collection {
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }

    subscript (offsetBy int: Int) -> Element? {
        let index = self.index(self.startIndex, offsetBy: int)
        return self[index]
    }
}

Maybe I need to clarify my intention. (Thanks to Nate.)

You can make your second subscript sort of safe:

extension Collection {
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }

    subscript (offsetBy int: Int) -> Element? {
        let index = self.index(self.startIndex, offsetBy: int)
        return self[safe: index]
    }
}

Even in such cases, you should better use different labels for your two subscript declarations to avoid ambiguity.

OOPer
  • 47,149
  • 6
  • 107
  • 142
  • I feel like the offsetBy version should use the safe method, passing the index you created instead of calling [] directly. This would help with the out of bounds issue, right? – Nate Apr 09 '19 at 03:27
  • @Nate, improving the defined method is not the main concern of this thread I think, but I do not understand what do you say as _the safe method_, you mean using `self[safe: index]` instead of `self[index]`? – OOPer Apr 09 '19 at 03:33
  • I’m not familiar with `subscript`. Is that how you define a [] method call in Swift? (I’m new to Swift but not new to programming.) If so, then yeah, that’s what I was getting at. I figured I’d mention it since you pointed out the crash. I may have missed the point you were making though. – Nate Apr 09 '19 at 03:36
  • **_Is that how you define a [] method call in Swift?_** Yes. Your comment is so suggesting as for a general programming topic, but not so useful as a comment in Swift programming. Unless you define a `subscript` with _safe_ feature, `Collection.subscript` may crash. – OOPer Apr 09 '19 at 03:41
  • These are very valid points. It is true that the second subscript is missing the safe, as @OOPer mentioned. Thank you for pointing that out. I figured these 2 extensions were different enough because of the type, and Swift could figure out which one to choose based on the type: myArray[safe: index] and myArray[safe: int] are different enough I think. Or at least it was in previous versions of Xcode... – invalidArgument Apr 09 '19 at 11:41