4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Swift6.2のうれしい変更点 ~Add Collection conformances for enumerated()~

Last updated at Posted at 2025-05-19

はじめに

Swiftでは、言語仕様のアップデートが日々プロポーサルという形で提案・議論されています。
Swift6.2では、便利なアップデートが多く実施されるようです。

今回は、Swift6.2で導入される予定のAdd Collection conformances for enumerated()というプロポーサルについてまとめます。
プロポーサルのリンクはこちら

TL;DR

enumerated()の返り値であるEnumeratedSequenceCollectionに準拠するようになります
これによって以下の嬉しさがあります

  • 特定の操作に対するパフォーマンスが大きく向上します
  • SwiftUIのListForEachに対して、Arrayでラップせずに直接EnumeratedSequenceを渡せるようになります。

プロポーサルの概要

プロポーサルのIntroductionの章をみると、次のように書かれています。

This proposal aims to fix the lack of Collection conformance of the sequence returned by enumerated(), preventing it from being used in a context that requires a Collection.

日本語に訳すと以下のようになるでしょうか。

この提案は、enumerated() によって返されるシーケンスが Collection に準拠していないという問題を修正し、Collection を必要とするコンテキストで使用できるようにすることを目的としています。

つまり、enumerated()が返すEmumeratedSequenceCollectionに準拠するということのようです。

enumerated()とはなにか

まずは、今回の議論の対象であるenumerated()について確認しましょう。
公式ドキュメントには以下のような説明がされています。

Returns a sequence of pairs (n, x), where n represents a consecutive integer starting at zero and x represents an element of the sequence.

つまり、Arrayに対して、({0からの連番}, {Arrayの要素})をもつEnumeratedSequenceを返すメゾットということのようですね。

簡単なコードで確認してみましょう。

let string = "Hello, World!"

for char in string.enumerated() {
    print(char)
}

上のコードの実行結果は、以下のようになります

(offset: 0, element: "H")
(offset: 1, element: "e")
(offset: 2, element: "l")
(offset: 3, element: "l")
(offset: 4, element: "o")
(offset: 5, element: ",")
(offset: 6, element: " ")
(offset: 7, element: "W")
(offset: 8, element: "o")
(offset: 9, element: "r")
(offset: 10, element: "l")
(offset: 11, element: "d")
(offset: 12, element: "!")

0からの連番にはoffset、Arrayの要素にはelementというラベルが与えられるようですね
繰り返し処理でindexを利用したい場合には非常に便利そうです

enumerated()がCollectionに準拠するとどうなるのか

このenumerated()Collectionに準拠すると、どのような利点があるのでしょうか?
プロポーサルのMotivationの章には3点が紹介されていたので、順に説明します。

EnumeratedSequenceが特定の操作を高速に実行できるようになる

プロポーサルでは次のような記述で解説されています。

(1000..<2000).enumerated().dropFirst(500) becomes a constant time operation.

EnumeratedSequenceCollectionに準拠していない場合、dropFirst(500)の実行時には、要素を1つずつ順にスキップする必要があり、O(n)の実行時間を要します。

Collectionに準拠している場合、dropFirst(500)は内部的にindexが500の要素からのスライス処理によってO(1)の実行時間になります。

このようなポインタの移動や範囲指定によって達成できる操作のパフォーマンスが大きく向上されられるようです。

reversed()の返り値がReversedColectionになる

プロポーサルでは次のような記述で解説されています。

"abc".enumerated().reversed() will return a ReversedCollection rather than allocating a new array.

Collectionへの準拠のないEnumeratedSequenceに対してreverse()を実行した場合、内部的にはすべての要素を走査してArrayに変換し、逆順に並べ直す必要があります。

Collectionへの準拠によって、ReversedCollectionにラップして返すだけで、reverse()の処理を完了でき、O(1)の実行時間で処理を完了できます。
中間生成物であるArrayも不要です。

こちらも、パフォーマンスの大きな向上が期待できそうですね。

SwiftUIのListやForEachでの利用がシンプルになる

プロポーサルでは次のような記述で解説されています。

SwiftUI’s List and ForEach views will be able to directly take an enumerated collection as their data.

個人的には、一番うれしい利点です。
SwiftUIでは、ListForEachに配列やコレクションを渡すことで要素を表示することができます。

しかし、EnumeratedSequenceCollectionに準拠していないために、直接ListやForEachEnumeratedSequence`を直接渡すことができませんでした。
これを解決するためにArrayでラップするなどの工夫をしている方が多いのではないでしょうか。

例えば、以下のViewはコンパイルエラーになりません。

struct ContentView: View {
    let strings = ["a", "b", "c"]
    
    var body: some View {
        VStack {
            ForEach(strings, id: \.self) { string in
                Text(string)
            }
        }
    }
}

次のViewはSwift6.2未満ではコンパイルエラーになります

struct ContentView: View {
    let strings = ["a", "b", "c"]
    
    var body: some View {
        VStack {
            // strings.enumerated()はEnumeratedSequenceを返すが、Collectionに準拠していないのでコンパイル不可
            ForEach(strings.enumerated(), id: \.offset) { string in
                Text(string.element)
            }
        }
    }
}

エラーコードが以下になっており、RandomAccessCollectionに準拠していないためにコンパイルできないことがわかります

Generic struct 'ForEach' requires that 'EnumeratedSequence<[String]>' conform to 'RandomAccessCollection'

これを回避するためには、Arrayでラップするなどの工夫が必要です。

struct ContentView: View {
    let strings = ["a", "b", "c"]
    
    var body: some View {
        VStack {
            // ArrayはCollectionに準拠しているのでコンパイルできる
            ForEach(Array(strings.enumerated()), id: \.self) { string in
                Text(string)
            }
        }
    }
}

ArrayはCollectionに準拠しているため、EnumeratedSequenceをラップすることで問題なくコンパイル可能です。

EnumeratedSequenceCollectionに準拠することで、Arrayでラップする必要がなくなり、より直感的な記述ができるようになります。

まとめ

今回は、Swift6.2で導入予定のAdd Collection conformances for enumerated()というプロポーサルについてまとめてみました。

enumerated()の返り値であるEnumeratedSequenceCollectionに準拠することで

  • これまで各要素を走査する必要があった処理の一部が、ポインタの移動や範囲指定によって効率的に実行できるようになる
  • ReversedCollectionを利用することで、逆順の生成が効率的になる
  • SwiftUIのListやForEachで、より直感的な記述ができるようになる

といった利点があることがわかりました!
今後indexを利用したViewの繰り返し処理でお世話になりそうな、ちょっと嬉しい変更ですね!

好評であればいくつかSwift6.2で採用されるプロポーサルをまとめようと思います。
ぜひいいねやストックなどよろしくお願いします!

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?