From d35267cc164e28bc0c991e80d950d4ac38a248ac Mon Sep 17 00:00:00 2001 From: Shubhransh Gupta <54713516+shubhransh-gupta@users.noreply.github.com> Date: Thu, 27 Aug 2026 23:53:15 +0530 Subject: [PATCH] Conditionally conform Chain2Sequence to MutableCollection (#217) --- Guides/Chain.md | 4 +-- Sources/Algorithms/Chain.swift | 35 +++++++++++++++++++-- Tests/SwiftAlgorithmsTests/ChainTests.swift | 17 ++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Guides/Chain.md b/Guides/Chain.md index ce5671cfd..15e5ede8e 100644 --- a/Guides/Chain.md +++ b/Guides/Chain.md @@ -30,8 +30,8 @@ public func chain(_ s1: S1, _ s2: S2) -> Chain2Sequence ``` The resulting `Chain2Sequence` type is a sequence, with conditional conformance -to `Collection`, `BidirectionalCollection`, and `RandomAccessCollection` when -both the first and second arguments conform. +to `Collection`, `BidirectionalCollection`, `RandomAccessCollection`, and +`MutableCollection` when both the first and second arguments conform. ### Naming diff --git a/Sources/Algorithms/Chain.swift b/Sources/Algorithms/Chain.swift index 2cda16f15..1b2624fd3 100644 --- a/Sources/Algorithms/Chain.swift +++ b/Sources/Algorithms/Chain.swift @@ -14,11 +14,11 @@ public struct Chain2Sequence where Base1.Element == Base2.Element { /// The first sequence in this chain. @usableFromInline - internal let base1: Base1 + internal var base1: Base1 /// The second sequence in this chain. @usableFromInline - internal let base2: Base2 + internal var base2: Base2 @inlinable internal init(base1: Base1, base2: Base2) { @@ -292,6 +292,37 @@ where Base1: BidirectionalCollection, Base2: BidirectionalCollection { extension Chain2Sequence: RandomAccessCollection where Base1: RandomAccessCollection, Base2: RandomAccessCollection {} +extension Chain2Sequence: MutableCollection +where Base1: MutableCollection, Base2: MutableCollection { + @inlinable + public subscript(i: Index) -> Base1.Element { + get { + switch i.position { + case .first(let i): + return base1[i] + case .second(let i): + return base2[i] + } + } + set { + switch i.position { + case .first(let i): + base1[i] = newValue + case .second(let i): + base2[i] = newValue + } + } + _modify { + switch i.position { + case .first(let i): + yield &base1[i] + case .second(let i): + yield &base2[i] + } + } + } +} + //===----------------------------------------------------------------------===// // chain(_:_:) //===----------------------------------------------------------------------===// diff --git a/Tests/SwiftAlgorithmsTests/ChainTests.swift b/Tests/SwiftAlgorithmsTests/ChainTests.swift index e5066e62e..82cd51f2d 100644 --- a/Tests/SwiftAlgorithmsTests/ChainTests.swift +++ b/Tests/SwiftAlgorithmsTests/ChainTests.swift @@ -70,4 +70,21 @@ final class ChainTests: XCTestCase { XCTAssertNil(j) } } + + func testChainMutableCollection() { + let a = [1, 2, 3] + let b = [4, 5, 6] + var c = chain(a, b) + + c[c.startIndex] = 10 + let secondStart = c.index(c.startIndex, offsetBy: 3) + c[secondStart] = 40 + + expectEqualSequences(c, [10, 2, 3, 40, 5, 6]) + + for i in c.indices { + c[i] *= 2 + } + expectEqualSequences(c, [20, 4, 6, 80, 10, 12]) + } }