1

I am currently trying to update a custom collection type to Swift 4.1. However, when I adhere to the documentation and implement all requirements for Collection and RangeReplaceableCollection, Xcode is still complaining that my type does not conform to RangeReplaceableCollection.

Here's an mcve for the problem (generously provided by Hamish, thank you for that :)

class Foo<Element : AnyObject> {
  required init() {}
  private var base: [Element] = []
}

extension Foo : Collection {
  typealias Index = Int

  var startIndex: Index {
    return base.startIndex
  }

  var endIndex: Index {
    return base.endIndex
  }

  func index(after i: Index) -> Index {
    return base.index(after: i)
  }

  subscript(index: Index) -> Element {
    return base[index]
  }
}

extension Foo : RangeReplaceableCollection {
  func replaceSubrange<C : Collection>(
    _ subrange: Range<Index>, with newElements: C
  ) where Element == C.Element {}
}

According to the documentation, the code should compile:

To add RangeReplaceableCollection conformance to your custom collection, add an empty initializer and the replaceSubrange(_:with:) method to your custom type. RangeReplaceableCollection provides default implementations of all its other methods using this initializer and method.

Unfortunately though, it doesn't. Instead, Xcode emits the following error message:

// error: type 'Foo<Element>' does not conform to protocol 'RangeReplaceableCollection'
// extension Foo : RangeReplaceableCollection {
// ^
//   Swift.RangeReplaceableCollection:5:26: note: candidate has non-matching type '<Self, S> (contentsOf: S) -> ()' [with SubSequence = Foo<Element>.SubSequence]
//   public mutating func append<S>(contentsOf newElements: S) where S : Sequence, Self.Element == S.Element
//                        ^
//   Swift.RangeReplaceableCollection:9:26: note: protocol requires function 'append(contentsOf:)' with type '<S> (contentsOf: S) -> ()'; do you want to add a stub?
//   public mutating func append<S>(contentsOf newElements: S) where S : Sequence, Self.Element == S.Element
//   

To make sure it's not an error in the documentation, I checked the source code of Swift 4.1 and found the default implementation of func append<S>(contentsOf newElements: S) where S: Sequence, Element == S.Element in RangeReplaceableCollection.swift, lines 442-452:

@_inlineable
public mutating func append<S : Sequence>(contentsOf newElements: S) where S.Element == Element {
  let approximateCapacity = self.count + numericCast(newElements.underestimatedCount)
  self.reserveCapacity(approximateCapacity)
  for element in newElements {
    append(element)
  }
}

Question:

  • Why does Xcode ask for an implementation of this function although a default implementation is provided?
  • How do I get my code to compile?
Hamish
  • 78,605
  • 19
  • 187
  • 280
Jan Nash
  • 1,883
  • 19
  • 30
  • Your link to the project does not work. Generally all relevant code must be included in the question itself (as offsite resources might become unavailable, making the question useless for future readers). – Martin R Apr 13 '18 at 12:43
  • You need to implement the requirement `mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C : Collection, Self.Element == C.Element` (not the generic variant over `RangeExpression`). Could you please provide a [mcve] so we can reproduce your issue? – Hamish Apr 13 '18 at 12:44
  • Sorry, I fixed the link. I am currently trying to implement a minimal, complete and verifiable example and will update the question accordingly. Thanks :) – Jan Nash Apr 13 '18 at 12:52
  • Here's an mcve for you: https://gist.github.com/hamishknight/a7a8b2b70c8eff9efec2b4e6a94338f8. Looks like a bug to me; if you uncomment the `required init` and `func append` it compiles (it also compiles if you mark the class as `final` or are in Swift 4.0.3). Looks like the generic placeholder in the requirement is the problem. By the looks of the error message I think this could actually be related to the change discussed in https://stackoverflow.com/q/49792626/2976878.... I'll file a bug when I get a moment. – Hamish Apr 13 '18 at 13:21
  • Thanks a bunch, that's kind of you. Meanwhile, I had also created an mcve, just without the inlined error message: https://gist.github.com/JanNash/ce344a40747b9fc38b83c14fa86247c2 // I'll try to salvage the question. – Jan Nash Apr 13 '18 at 13:27
  • Bug filed: https://bugs.swift.org/browse/SR-7429 – Hamish Apr 13 '18 at 13:36
  • Great! Do you think it makes sense trying to improve the question, or should we rather vote to close/delete? – Jan Nash Apr 13 '18 at 13:38
  • @JanNash That's up to you; if you update it with an mcve, it could be a useful Q&A to point people having the same issue to. – Hamish Apr 13 '18 at 13:53
  • Agreed, done, open for improvement suggestions :) – Jan Nash Apr 13 '18 at 14:39

1 Answers1

3

Why does Xcode ask for an implementation of this function although a default implementation is provided?

This is due to a bug in the TypeChecker, introduced in Swift 4.1:
[SR-7429]: Cannot conform a non-final class to a protocol with a defaulted requirement with a generic placeholder constrained to an associated type

How do I get my code to compile?

Until the bug is fixed, there's currently three ways to get it to compile.

  • Implement these two functions:

    • required init<S : Sequence>(_ elements: S) where Element == S.Element {}
    • func append<S : Sequence>(contentsOf newElements: S) where Element == S.Element
  • Mark the class as final.

  • Implement the collection type as a struct. For this, the required init() {} must be removed.

Jan Nash
  • 1,883
  • 19
  • 30