Swift编译器为何在泛型Element已显式约束时仍抛类型推断错误?
I see you're hitting some tricky compiler errors when trying to use Collection methods on your WeakArray type—let's break down what's going on and how to fix it properly, without relying on the temporary workaround.
What's Causing the Errors?
The core issue stems from two key problems:
- Generic Naming Conflict: Your
WeakArrayusesElementas its generic parameter, and theCollectionprotocol also has an associated type namedElement. When you implementCollectionwithout explicitly specifying this associated type, the compiler gets confused betweenWeakArray<Element>andCollection.Element(which isElement?from your subscript return value). - Private Property Type Inference: When
itemsis private, the compiler can't properly resolve theWeakReftype constraints when you call Collection methods likeforEach, leading to the "cannot infer type" error.
Proper Solutions
Solution 1: Avoid Naming Conflict with Explicit Associated Type
Rename your WeakArray generic parameter to avoid clashing with Collection.Element, then explicitly define the protocol's associated type. This keeps items private and fixes the inference issue:
final class WeakRef<T: AnyObject> { weak var value: T? init(_ value: T) { self.value = value } } struct WeakArray<T: AnyObject> { private var items: [WeakRef<T>] = [] // Keep items private init(_ elements: [T]) { items = elements.map { WeakRef($0) } } } extension WeakArray: Collection { // Explicitly define Collection's Element type as optional T typealias Element = T? var startIndex: Int { items.startIndex } var endIndex: Int { items.endIndex } subscript(position: Int) -> Element { items[position].value } func index(after position: Int) -> Int { items.index(after: position) } }
Now you can use Collection methods without any issues, even with items as private:
var objects: WeakArray<UIViewController> = WeakArray([]) objects.forEach { $0?.view.backgroundColor = .white }
Solution 2: Keep Original Naming with Clear Type Clarity
If you prefer to keep Element as your WeakArray generic parameter, explicitly clarify the Collection.Element type using Optional<Element> to eliminate confusion:
struct WeakArray<Element: AnyObject> { private var items: [WeakRef<Element>] = [] init(_ elements: [Element]) { items = elements.map { WeakRef($0) } } } extension WeakArray: Collection { typealias Element = Optional<Element> var startIndex: Int { items.startIndex } var endIndex: Int { items.endIndex } subscript(position: Int) -> Element { items[position].value } func index(after position: Int) -> Int { items.index(after: position) } }
This achieves the same result while retaining your original generic parameter name.
Why the Temporary Workaround Worked
When you made items public and used a for-loop first, you gave the compiler enough explicit context to resolve the generic type constraints manually. Once it inferred the correct type for items, it could then handle Collection methods properly. The solutions above eliminate the need for this hack by defining the type relationships upfront.
Environment Compatibility
These fixes work for Xcode 9.3.1 (9E501) and Swift 4.1, and are compatible with newer Swift versions as well.
内容的提问来源于stack exchange,提问作者Serj

