The problem is really in the line
let wrapped = Wrapper<DataSource, String>(value: MyDataSource(), val2: "Ignored")
to which the easiest solution is to let Swift just infer the generics (as others have mentioned).
To see why it's a problem I think the following example makes it a bit easier to understand the error message you're getting:
error: in argument type 'Wrapper<DataSource, String>' (aka 'Wrapper<Blah & Blih, String>'),
'DataSource' (aka 'Blah & Blih') does not conform to expected type 'Blah'
Consider:
protocol Foo {
func foo() -> Bool
init()
}
protocol Bar {
func bar() -> Bool
init()
}
typealias FooBar = Foo & Bar
struct Concrete: FooBar {
func foo() -> Bool { return false }
func bar() -> Bool { return true }
init() {}
}
func create<B>(type: B.Type) -> B where B: Foo {
return B()
}
let a = create(type: Concrete.self)
let b = create(type: FooBar.self)
Here the create(type:)
function is essentially your other(id:w:)
. It takes different arguments, however the situation with the generics is the same. I've used meta-types here because Swift needs something to deduce the generic parameter, but we don't actually use the argument in the function so really any method of specifying the type will yield the same result.
The line let a = create(type: Concrete.self)
succeeds and returns an instance of Concrete
. However let b = create(type: FooBar.self)
does not:
error: in argument type 'FooBar.Protocol' (aka '(Bar & Foo).Protocol'), 'FooBar' (aka 'Bar & Foo') does not conform to expected type 'Foo'
let b = create(type: FooBar.self)
^
If we were permitted to write the generic parameters for functions explicitly, it is easy to see that the above call would be identical to:
let b = create<FooBar>(type: FooBar.self)
We can then see what the problem would be if Swift allowed the function call: when the generic parameter is FooBar
, which B
do we mean if we use the type in the function body?
i.e. what would return Foobar()
mean?
The example is contrived, in that init()
is actually part of the interface of Foo
(which constrains the generic parameter), so we know how to instantiate any given Foo
. But is is hopefully clear that when passing a protocol FooBar
, there are cases that can arise when Swift doesn't know which implementation of it to use.
In your example, when you define
let wrapped = Wrapper<DataSource, String>(value: MyDataSource(), val2: "Ignored")
And then call
other(id: "bar", w: wrapped)
If we were to write the generic parameters explicitly, since wrapped
is of type Wrapper<DataSource, String>
, this would be
other<DataSource, String>(id: "bar", w: wrapped)
And so this is the same situation as before, a protocol has been used in a constrained generic parameter – which implementation would we use if we referred to it directly?
I think that in theory your particular usage would not run into the problem demonstrated because your protocols do not have static requirements (init acts like a static requirement because you'd use it by referring to the type), which means that all requirements you could possibly use require an instance (which would always have a concrete implementation).
I think that this could potentially be something that could be relaxed as a restriction (i.e. allow this behaviour when protocols only have instance requirements), some very careful consideration would need to be done though.
Hopefully this at least illustrates why, in general, if you place constraints on generics then it might allow problems to arise when you try to use a protocol (without a concrete implementation) as a type inside the function (hence why the error exists).