我如何创建一个 getter 的下标 returns 是可选的,而 setter 是 returns 非可选的

How can I create a subscript with a getter that returns an optional, but a setter that returns a non-optional

我正在编写一个名为 TriangularArray<T> 的自定义集合。它代表这样的结构:

     x
    x x
   x x x
  x x x x
 x x x x x

其中每个 x 都是数组中的一个元素。我可以访问具有行号和索引号的元素,它们都是从零开始的。例如,访问以下的 (4, 2):

     a
    b c
   d e f
  g h i j
 k l m n o

将导致 m(第 5 行,该行中的第三个值)。

我用了一个[[T]]作为后备数组,我写了一个这样的下标:

subscript(_ row: Int, _ index: Int) -> T? {
    get {
        // innerArray is the [[T]] used for backing
        if row < 0 || row >= innerArray.count {
            return nil
        }
        if index < 0 || index > row {
            return nil
        }
        return innerArray[row][index]
    }

    set {
        if row < 0 || row >= innerArray.count {
            return
        }
        if index < 0 || index > row {
            return
        }
        innerArray[row][index] = newValue!
    }
}

逻辑是,如果您访问不存在的行和索引,例如 (1, 3),下标将 return nil。但是,通过使下标return T?,setter中的newValue也成为可选的,我必须强制解包它。

我真的很想对这种事情进行编译时检查:

triangularArray[0, 0] = nil // this should be a compile time error

我试着在 Google 上查找这个,但我只找到这个 ,这是非常过时的。我们当然可以在 Swift 4.2 中做得更好,对吧?

只有 Swift 4.2 可以帮助您在我们的代码中标记此类问题,我认为 4.1 或更低版本不可能。

Swift 4.2 例子:

#if true
   #error("add here your compiler error")
#endif

不幸的是,Swift(以及其他使用动态数组的语言)不可能做到,因为数组的大小是动态的并且在编译时不知道(它可以用程序为 运行).
时的任意值 例如,如果 TriangularArray<T> 的大小为 1,则 [0, 0] 是一个有效元素 ,而编译器无法事先知道。
在标准库中,当您尝试访问像 array[-1] 这样的数组时,不会出现编译时错误 - 这将始终编译但始终会导致运行时错误。

我认为您目前使用可选的解决方案似乎是这里的最佳方案。您还与 Array 的工作方式保持一致,并在给出无效下标时触发 fatalError
另一种方法是在 TriangularArray 中创建一个自定义 Index 结构来表示三角形的索引,并且可以选择从 xy 值构建(尽管这可能会使事情复杂化相当多)。

PS:这个答案假定 TriangularArray<T> 可以有一个任意高度的三角形(可以在运行时指定的高度),因为它没有在问题中指定。如果高度是在编译时定义的,那么边界可以是硬编码的,就像@Raul Mantilla 提到的那样 #error 可以使用。