Reputation: 614
I've been dabbling with Swift recently, and I've hit a weird stumbling block with type constraints not operating as I would expect they should (in comparison to say, Scala).
protocol Foo {
typealias T: Hashable
func somethingWithT() -> T
}
struct Bar: Foo {
typealias T = Int
func somethingWithT() -> T { return 1 }
}
func baz() -> [Foo] {
var myBars = [Foo]()
myBars.append(Bar())
return myBars
}
This throws an error in XCode:
Any ideas what is going on here? I want T
to be hashable for use as a key in a dictionary, but from what I've read Hashable
has a reference to Self
somewhere, so it can only be used as a generic constraint, thus removing my ability to just have a [Foo]
.
I want to have a list of things that do Foo
, but at this rate it seems I'll either have to remove the Hashable
constraint or make it less generic..
I've tried cleaning my project and re-making, but no dice :(
Upvotes: 0
Views: 76
Reputation: 13253
The problem here is that the type of T
(of the protocol) has to be known at runtime. Due to the lack of generics with type aliases you can work around that by making an AnyFoo
type (like in the standard library AnyGenerator
and AnySequence
):
protocol Foo {
typealias T: Hashable
func somethingWithT() -> T
}
struct AnyFoo<T: Hashable>: Foo {
let function: () -> T
init<F: Foo where F.T == T>(_ foo: F) {
// storing a reference to the function of the original type
function = foo.somethingWithT
}
func somethingWithT() -> T {
return function()
}
}
struct Bar: Foo {
typealias T = Int
func somethingWithT() -> T { return 1 }
}
// instead of returning [Foo] you can return [AnyFoo<Int>]
func baz() -> [AnyFoo<Int>] {
var myBars = [AnyFoo<Int>]()
// converting the type or Bar
myBars.append(AnyFoo(Bar()))
return myBars
}
This is not a generic function but you can convert any Foo
type with the same T
to the same AnyFoo
type
Upvotes: 2
Reputation: 1
myBars is an array of T: Foo. You could certainly pass in a Bar, because it satisfies the constraint on T. But so could another concrete type. Since T must be a concrete type, you can't arbitrarily put a Bar on there without guaranteeing that the only thing you'll put in there are Bars. And the only way to do that in Swift with this function signature would be to pass in a Bar.
I think maybe what you want is an array of protocol objects, not a generic array.
Upvotes: 0