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.

Terms of Service

Privacy Policy

Cookie Policy