Tetigi
Tetigi

Reputation: 614

Swift Type Constraints not working as expected

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:

enter image description here

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

Answers (2)

Qbyte
Qbyte

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

swiftGuest
swiftGuest

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

Related Questions