[any TextContainer], the existential box, not on arrays of any type that conforms to TextContainer. Move the constraint onto the method itself with where Element == [T] and T: TextContainer, and keep the old extension only for genuinely heterogeneous [[any TextContainer]] data.Why this happens
The extension is written like this:
public typealias region = Array<any TextContainer>
public extension Array where Iterator.Element == region {
func everyRegionString(_ separator: String = " ") -> String { ... }
}
That where clause does not mean “arrays of anything that conforms to TextContainer“. It means “arrays whose element type is exactly Array<any TextContainer>“. The element type is pinned to one concrete type, and [T] is not that type unless T happens to be any TextContainer.
any TextContainer is an existential: a box holding some unknown concrete type at runtime. It is not the same thing as a generic parameter T: TextContainer, which is a concrete type known at compile time. Worse, any TextContainer does not itself conform to TextContainer — your protocol refines Equatable, which has a Self requirement, so the box cannot satisfy it. That is why the compiler says the two array types must be equivalent: it cannot treat [[T]] as [[any TextContainer]], and it will not silently box each element for you.
So T: TextContainer does not “fall within” any TextContainer. They are different kinds of type, and the extension has to be written for the one you actually pass in.
How to fix it
1. Move the constraint from the extension to the method
Leave the extension unconstrained and put the requirement in a where clause on each method. The method becomes generic over T, and Element == [T] ties it to the array you are calling it on.
public protocol TextContainer: Equatable, CustomStringConvertible {
var text: String { get set }
}
public extension Array {
func everyRegionString<T: TextContainer>(_ separator: String = " ")
where Element == [T] {
var result = ""
for region in self {
for detection in region {
result += detection.text + separator
}
}
return result
}
func individualRegionAsString<T: TextContainer>(
_ regionIndex: Int,
separator: String = " "
) where Element == [T] {
var result = ""
for detection in self[regionIndex] {
result += detection.text + separator
}
return result
}
}
Now both of these compile:
struct Detection: TextContainer {
var text: String
var description: String { text }
}
func someMethod<T: TextContainer>(regions: [[T]]) -> String {
regions.everyRegionString()
}
let concrete: [[Detection]] = [[Detection(text: "a")]]
let s = concrete.everyRegionString(", ")
Note the parentheses: regions.everyRegionString without them refers to the function value, not the result, and produces a different error.
If you want the extension to accept any collection of text containers rather than strictly [T], constrain on Collection instead:
extension Array where Element: Collection, Element.Element: TextContainer {
func everyRegionString(_ separator: String = " ") -> String {
flatMap { $0 }.map(\.text).joined(separator: separator)
}
}
This still requires a concrete conforming element type, so it will not accept [[any TextContainer]] either.
2. Keep the existential version for heterogeneous data
If a call site really does hold [[any TextContainer]] — mixed concrete types in the same array — the generic method cannot help, because there is no single T. Keep the original extension for that case and give it a distinct name or a distinct element type so the two overloads never compete:
public typealias Region = [any TextContainer]
public extension Array where Element == Region {
func everyRegionString(_ separator: String = " ") -> String {
flatMap { $0 }.map(\.text).joined(separator: separator)
}
}
Rename the typealias to Region while you are there — Swift type names are UpperCamelCase, and a lowercase region reads like a variable at every call site.
3. Boxing works, but only as a last resort
You can force the generic array into the existential shape by wrapping every element:
func someMethod<T: TextContainer>(regions: [[T]]) -> String {
regions
.map { $0.map { $0 as any TextContainer } }
.everyRegionString()
}
This compiles, but each as any TextContainer allocates an existential box on the heap. Doing it inside a loop over thousands of detections costs real time and memory. Prefer option 1 and box only at a boundary where you genuinely need type erasure.
How to prevent it
- Read
where Element == Xas “element type is exactly X”, never as “element type conforms to X”. - When a method needs to work across conforming types, make the method generic and constrain it with
where Element == [T]. - Remember that
any Pis a box, not a conforming value; it does not satisfyPwhenPhasSelfor associated type requirements. - Drop
EquatablefromTextContainerif you never compare containers — it removes aSelfrequirement and makes the existential easier to use elsewhere, though it still won’t make[[any TextContainer]]interchangeable with[[T]]. - Call the method with parentheses; a bare
regions.everyRegionStringis a function reference.
FAQ
Why doesn’t T: TextContainer satisfy a parameter typed any TextContainer?
Because they are different things. T is a concrete type chosen by the caller and known at compile time; any TextContainer is a runtime box that erases which concrete type it holds. An array of T and an array of boxes are distinct types with distinct memory layouts.
Does any TextContainer conform to TextContainer?
Not here. Your protocol refines Equatable, which has a Self requirement, so the existential cannot conform to it. Even for simpler protocols, treating the box as a conforming value is not the same as using a generic parameter.
Can I have both extensions on Array?
Yes, as long as their element types differ — one constrained with where Element == [T] on the method, the other with where Element == [any TextContainer] on the extension. They resolve to different call sites, so there is no ambiguity.
Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.
![Fix: Array Extension Requires '[T]' and '[any TextContainer]' Match Fix: Array Extension Requires '[T]' and '[any TextContainer]' Match](https://apple-developers-inc.com/wp-content/uploads/2026/10/fix-array-extension-requires-t-and-any-textcontainer-match.jpg)