Why can my simple protocol be used only as a general constraint?

I'm trying to do some protocol composition for use in dependency injection, but I'm having a problem that I suspect may not have the solution I would like, but for which I see no logical reason:

protocol DM1 {
    func sayHi() -> Void
}

protocol DM2 {
    func sayHello() -> Void
}

protocol VM1 {
    typealias T: DM1
    var dm: T { get }
}

protocol VM2 {
    typealias T: DM2
    var dm: T { get }
}

protocol RT: VM1, VM2 {

}

class James {
    let rt: RT

    init(rt: RT) {
        self.rt = rt
    }
}

      

The above code results in the error "The" RT "protocol can only be used as a general constraint because it has its own or related type requirements" in the instance variable rt

and the instantiation parameter for James

. I really don't understand why I can't use this common requirement in the classroom James

.

I originally did something like:

protocol DM1 {
    func sayHi() -> Void
}

protocol DM2 {
    func sayHello() -> Void
}

protocol VM1 {
    var dm: DM1 { get }
}

protocol VM2 {
    var dm: DM2 { get }
}

protocol RT: VM1, VM2 {

}

struct Fred: DM1, DM2 {
    func sayHi() {
        println("hi")
    }
    func sayHello() {
        println("hello")
    }
}

struct Bob: RT {
    let dm: Fred
}

class James {
    let rt: RT

    init(rt: RT) {
        self.rt = rt
    }
}

      

But this fails because "Type" Bob "does not conform to protocol" VM1 "(and VM2

) which I can understand as my protocol requires the variable to have a specific protocol type, not some instance type that conforms to that So the above version had to be around.

Does anyone have a solution for what I want to do (to be able to create a specific structure, which corresponds to rt

having as a property of dm

a specific structure, which is consistent with both DM1

, and so DM2

)?

+3


source to share


1 answer


protocol RT

inherits from protocol VM1

and protocol VM2

that have typealias

requirements.

A demanding protocol typealias

can only be used as a type constraint, not as a type.

Even a simple protocol like this ...

protocol MyProtocol {
    typealias Empty
}

      

... (which is completely useless) can only be used as a type constraint.

See this link that might be helpful.

EDIT:

I'll do my best to explain why requirements protocols typealias

can only be used as type constraints.

Think of protocol as a contract. This protocol promises to feed a modified value with a promise

type name Int

:

protocol PromiseIntType {
    var promise: Int { get set }
}

      

The contract that does PromiseIntType

is explicit about everything you need to know in order to use it as a type with respect to the promises it does. So, if you have a class like this ...

class A {
    var contract: PromiseIntType
}

      

... you know that if you write this ...

let intValue = A().contract.promise

      

... what intValue

will happen Int

. And if you want to set the value of the type property promise

, you know what you will need to specify Int

for the new value:

let a = A()
a.contract.promise = 100

      

All the terms of the contract are known in advance, and you know in advance what types of promises are created and what types you work with.

A PromiseIntType

can be used exactly as if it were defined as an actual type, for example:

struct PromiseInt {
    var promise: Int
}

      

Now add the requirement typealias

to the mix:

protocol PromiseSomeType {
    typealias Surprise
    var promise: Surprise { get set }
}

      



What promise does it make PromiseSomeType

? It says it will provide a mutable value called promise

, but it doesn't tell you what type that value will be. All of this tells you that whatever it provides will be Surprise

. Not all the terms of the contract are known in advance. Some details will be filled in later.

But that makes it impossible to use PromiseSomeType

as a type. Take a look at this class and ask yourself what you can do with the property contract

:

class B {
    var contract: PromiseSomeType
}

      

For example, how would you do it?

let b = B()
b.contract.promise = <What type goes here?>

      

What type value do you get if you try to access a property promise

?

let someValue = b.contract.promise // What type is someValue?

      

[Additional EDIT:

How to use someValue

? If it is Int

, you can do this:

let newValue = someValue + 12

      

But you cannot know at compile time if it someValue

is Int

or not. Swift insists on knowing the type of each constant, variable, and object at compile time, so that he can check if the actions you are doing by type are legal. If it is to defer these definitions at runtime, illegal operations can cause the entire program to crash and we lose the benefits of type safety.

/ Additional editing]

You fill out the details of the contract PromiseSomeType

when you create the actual type that fulfills the contract:

struct PromiseInt: PromiseSomeType {
    var promise: Int
}

      

PromiseInt

says that he will fulfill the contract PromiseSomeType

and he will fill in the missing information about what type the property will have promise

.

Using it PromiseSomeType

as a type constraint might seem like it is just causing string ambiguity:

class C<T: PromiseSomeType> {
    var contract: T
}

      

In this case, the contract details are filled in when the generic type is instantiated and the actual type you are using is specified:

let c = C<PromiseInt>(contract: PromiseInt(promise: 0))
c.contract.promise = 100   // You know that promise is of type Int

      

In any case, before you can actually use an object, all the type information must be known.

I suppose Swift is type safe. You cannot create ambiguous types. The protocols they use typealias

are ambiguous and therefore cannot be used as types, but only as type constraints.

+7


source







All Articles