Ambiguous type error prompted when different implementations are provided for different constraints in a generic type

I was trying to provide different implementations for a struct if its generic type is satisfied with different conditions.

Here is a demo example to illustrate my problem:

public protocol FooProtocol {
    /// `Bar` is my own type, using `String` here for demo purpose
    typealias Bar = String
    func to() -> [Bar]
    init?<C: Collection>(collection: C) where C == C.SubSequence, C.Element == Bar
}

public struct Foo<Value> {
    let value: Value
}

extension Foo: FooProtocol where Value: Numeric {
    public func to() -> [Bar] {
        return ["\(self.value)"]
    }

    public init?<C: Collection>(collection: C) where C == C.SubSequence, C.Element == Bar {
        return nil
    }
}

extension Foo where Value: RangeReplaceableCollection, Value.Element: FooProtocol {
    public func to() -> [Bar] {
        return self.value.reduce(into: []) { $0 += $1.to() }
    }

    public init?<C: Collection>(collection: C) where C == C.SubSequence, C.Element == Bar {
        self.value = .init()
    }
}

And when I do following:

let intFoo: Foo<Int> = .init(value: .max) /// give me instance of Foo<Int>
let intTo = intFoo.to() /// give me value of ["9223372036854775807"]
let intBack = Foo<Int>(collection: intTo)?.value
/// Should give me `nil`, however...
/// error: expression type 'Foo<Int>?' is ambiguous without more context

So why is the ambiguity? The generic type is Int, which means it should use the initializer in the first extension where the Value is a Numeric, unless the Int is actually a RangeReplaceableCollection at the same time?

Here's what I get on master:

/Users/suyashsrijan/Desktop/test.swift:34:15: error: initializer 'init(collection:)' requires the types '[String]' and 'ArraySlice<String>' be equivalent
let intBack = Foo<Int>(collection: intTo)?.value
              ^
/Users/suyashsrijan/Desktop/test.swift:17:12: note: where 'C' = '[String]', 'C.SubSequence' = 'ArraySlice<String>'
    public init?<C: Collection>(collection: C) where C == C.SubSequence, C.Element == Bar {
1 Like

Thanks a lot!!

Seems removing that C == C.SubSequence constraint is the fix.

But I do think Collection needs that constraint to perform func removeFirst(_:), and Array has that function, so why is Array’s SubSequence not equal to itself?

OK.... it seems Array has its own implementation of that function.