0

I want to create strings depending on types (parts of URLs, if you must know).

Consider this exemplary code:

import Foundation


protocol TestP {
    var p: Int { get }
}
protocol TestQ: TestP {
    var q: Int { get }
}
struct TestR: TestQ {
    var p = 1
    var q = 2
}

func toDescription<T: TestP>(type: T.Type) -> String {
    switch type {
        case is TestR.Type: return "Arrrr"
        default: return "unsupported"
    }
}

This seems reasonably nice; I didn't need to rely on unsafe measures (strings) nor did I need a separate enum.

Let's look at some usage example:

func example1<T: TestP>(val: T) {
    print("Processing \(toDescription(type: T.self))")
}

func example2() {
    print("Processing \(toDescription(type: TestR.self))")
}

func example3() {
    print("Processing \(toDescription(type: TestQ.self))")
}

While the first two functions are fine (the generic version is particularly nice!), the third does not compile:

Error: in argument type TestQ.Protocol.Type, TestQ.Protocol does not conform to expected type TestP

TestP.Type and TestP.Protocol do not work as parameters, either.

How can I pass protocol types to a (generic) function?

Raphael
  • 9,779
  • 5
  • 63
  • 94
  • 1
    This doesn't compile for good reason – say `TestP` had a `static` requirement. You are able to call that requirement on `type` from within `toDescription` – but if you were able to pass in `TestQ.self`, there'd be no implementation to call. – Hamish Feb 28 '17 at 13:16
  • This is part of a bigger restriction (in order to prevent these kinds of unsafe situations, but is otherwise full of edge cases that are perfectly safe), which is that [protocols don't conform to themselves](http://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself) – so you cannot use `TestQ` as a type that conforms to `TestP`. – Hamish Feb 28 '17 at 13:17
  • @Hamish I see. I guess I'd expect to be able to write `func f(type: T.Protocol)` then, in which case the compiler could check that I don't call static members on a protocol type. (Well, even in the version I wrote it could prevent me from accessing static members because it might fail.) – Raphael Feb 28 '17 at 14:13
  • Fun fact: if you switch over such parameter `type`, pattern `is TestQ.Type` does not elicit a warning "never matches". – Raphael Feb 28 '17 at 14:21
  • If protocols did conform to themselves, then yes I would expect something like `func f(type: T.Protocol)` to work (although it would disallow your first two examples from working) – Hamish Feb 28 '17 at 14:22
  • The pattern `is TestQ.Type` should be valid to match against a `type` of `T.Type` – it will match against a metatype of a type that conforms to `TestQ`, e.g `TestR.self`. – Hamish Feb 28 '17 at 14:26

1 Answers1

-2
protocol TestP {
    var p: Int { get }
}
protocol TestQ: TestP {
    var q: Int { get }
}
struct TestR: TestQ {
    var p = 1
    var q = 2
}

struct TestS: TestP
{
    var p = 42
}

func toDescription<T: TestP>(type: T.Type) -> String
{
    switch type
    {
    case let x where x == TestR.self:
        return "Arrr"
    default:
        return "Unsupported"
    }
}

print (toDescription(type: TestR.self)) // Arrr
print (toDescription(type: TestS.self)) // Unsupported
XmasRights
  • 1,427
  • 13
  • 21
  • 1
    This doesn't address the problem OP is having. OP is trying to pass `TestQ.self` into `toDescription(type:)`, but it's yielding a compiler error. – Hamish Feb 28 '17 at 16:08
  • 2
    Also, code-only answers rarely provide much insight for lack of explanation. – Raphael Feb 28 '17 at 16:29