有没有理由为什么array [index]没有返回一个可选的?

时间:2015-12-17 11:03:24

标签: swift

我觉得array[index]应该返回一个可选项,因为索引可能超出范围。

这将允许代码如:

if let object = array[index] {
  // Do stuff
}

使用扩展方法很容易做到,但是知道这种情况的真正原因会很有用。

5 个答案:

答案 0 :(得分:31)

这是my first Swift radars中的一个,因为"行为正确而被关闭。"还讨论了in the dev forums。正如Dave Abrahams所说:

  

至于理由,拥有索引在范围内的静态知识是很容易和非常普遍的....在这种情况下,建立索引有效的前提条件要好得多,以便常见的用例不会必须在语法上应对不可能发生的失败。将其与键上的字典索引进行对比,通常不知道键是否已经存在于字典中。

随着我在Swift中的成长经验越来越丰富,我已经同意了。我有时希望有一个"安全的下标" (如Mike Ash's)内置,但我已经同意它不应该是默认值。

将其设为默认值会使Arrays非常难以使用,这不仅仅是因为所需的解包,而是因为索引类型不再是Intsubscript(Index)必须返回Element(不是Element?)。这就是为什么词典索引不是Key;它是DictionaryIndex<Key,Value>。创建一个特殊的ArrayIndex可能会产生很多烦人的副作用。 (也许它最终都会成功,但它是否值得考虑是值得怀疑的。)

这里真正的教训是你应该避免任意下标Arrays。如果可行,您应该更喜欢将其用作CollectionType。这意味着仅使用您获取的索引(例如indexOfindices)进行订阅,并强烈支持迭代(for-inmap)而不是下标。使用xs.first而不是xs[0]。如果您将其视为集合而不是数组,那么您将获得您所描述的安全性,同时在您需要解决特殊问题时仍然可以使用下标,您知道下标在范围内。

这是一个说明性的例子。考虑这个常见的循环,您可能认为需要下标:

let xs = [1,2,3]

for i in 0..<xs.count {
    print("\(i): \(xs[i])")
}

我们可以做得更好一点,不依赖于我们对数组索引的特殊知识,并使其适用于所有集合:

for i in xs.indices {
    print("\(i): \(xs[i])")
}

但即便如此也是如此。我们可以做得更好,并使其适用于所有序列:

for (i, x) in xs.enumerate() {
    print("\(i): \(x)")
}

不需要下标。

答案 1 :(得分:2)

鉴于在user3441734下的详尽讨论:答案(我们应该在聊天中采取这种方式......),我觉得我必须说清楚我认为user3441734试图做的有点好的观点。

首先,请注意问题包括

  • 是什么原因让Swift让一个无效索引情况就像那样(抛出异常,超出范围),而不是返回一个可选的?

Rob的答案很详尽,并且很好地解决了这个问题,但我确实认为user3441734:s的答案至少应该得到一个状态&gt; = 0,因为它确实包含了另一个原因,为什么假设的情况&#34; swift让invalid-index返回一个可选的&#34; 可能不是一个好主意。请注意,这绝不是 &#34; ......真正的原因......&#34; 快速不要让无效指数返回为零,但我认为这一点应该是&gt; = 0-投票。

问题不包括:

  • 如果索引超出范围,是否可以获取可选项而不是异常。

问题作者自己陈述了&#34;这很容易用扩展方法&#34;

所以,有了这个,让我们来看看user3441734的答案,并试着让它更清楚一点,他/她试图指出我们。

我们将假设我们处于并行Swift Universe,,其中无效索引情况(wrt数组)被视为选项并返回nil ,我们将分析以下表达式。< / p>

// Lets assume the following is all in the scope of some function
var arr: Array<Int?> = []
arr.append(1)
arr.append(nil)
arr.append(3)
print("\(arr)") // Optional(1), nil, Optional(3)

// lets say the function randomIntegerTwoOrFour() -> Int returns, 
// randomly, either value 2 or value 4.
let ourMagicAndAtCompileTimeUnknownIndex = randomIntegerTwoOrFour()

// now, in our parallel Swift universe, say we want
// use an if let clause on our array at our magic index
if let n = arr[ourMagicAndAtCompileTimeUnknownIndex] {
    // ...
}
else {
    // lets say we're note careful here, and think that we've
    // entered this clause because of a nil valued member of
    // our array. If we try to use our magic index for non-nil
    // assigment, how would parallell universe Swift 2 handle this?

    // We could of course try some downcasting to infer actual, 
    // (optional) type ourselves, but this is not very "Swifty" 
    // w.r.t. if let clauses.
}

// on the other hand, say we want to guard against invalid-index
// in our array, using a guard let clause 
guard let n = arr[ourMagicAndAtCompileTimeUnknownIndex] else {
    print("Out of bounds!") 
         // or are we ...? Careful parallel Swift universe programmer!

    // Naturally, we could (and should, in parallell Swift 2 universe), 
    // look closer are what type n is at this point, but this is also
    // not very "Swifty" in this context). 
    return
}

总结一下,这是我从user3441734得到的答案:我认为它应该不会少于,也许不会超过0分,但是,不要被拒绝投票。

答案 2 :(得分:1)

可能是设计上的。由于在数组中获取项目数量非常容易,如果您将从0 to count-1进行迭代,则总会有一个vaule。如果array[index]可以返回nil,那么每次遍历数组时都必须打开一个可选项,这对于ass:)来说很痛苦。

答案 3 :(得分:0)

试试看这个

CommandBar
我认为,现在很容易理解原因。即使您的索引有效,您仍然可以收到

我知道类型,但是

var arr: Array<Int?> = []
arr.append(1)
arr.append(nil)
arr.count == 2
let n = arr[1] // nil

答案 4 :(得分:-1)

这取决于数组元素的类型。例如,如果您将数组定义为

var arr:[String?] = Array<String?>(count: 5, repeatedValue: "SomeString")

然后array[index]将是可选的。

但本质上array[index]是一个非可选值,因为访问其边界之外的数组本身会引发异常,更不用说获取它的值了。所以你不会过去阅读元素本身。