Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

Tags:

swift

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:

type 'Self' constrained to non-protocol type 'Foo'

struct Foo: MyProtocol {
    let myVar: String

    init(myVar: String) {
        self.myVar = myVar
    }
}

protocol MyProtocol {
    func bar()
}

extension MyProtocol where Self: Foo {
    func bar() {
        print(myVar)
    }
}

let foo = Foo(myVar: "Hello, Protocol")
foo.bar()

I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?

like image 847
Christopher Avatar asked Sep 30 '15 10:09

Christopher


2 Answers

This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.

The correct way to achieve what you described would be something like equality sign like:

extension MyProtocol where Self == Foo {
    func bar() {
        print(myVar)
    }
}

But this doesn't compile for some stupid reason like:

Same-type requirement makes generic parameter Self non-generic

For what it's worth, you can achieve the same result with the following:

protocol FooProtocol {
  var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
  let myVar: String
}

protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
  func bar() {
    print(myVar)
  }
}

where FooProtocol is fake protocol which only Foo should extend.

Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.

like image 104
Daniel Shin Avatar answered Oct 17 '22 10:10

Daniel Shin


I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:

Where Clauses

You can specify additional requirements on type parameters and their associated types by including a where clause after the generic parameter list. A where clause consists of the where keyword, followed by a comma-separated list of one or more requirements.

The requirements in a where clause specify that a type parameter inherits from a class or conforms to a protocol or protocol composition. Although the where clause provides syntactic sugar for expressing simple constraints on type parameters (for instance, T: Comparable is equivalent to T where T: Comparable and so on), you can use it to provide more complex constraints on type parameters and their associated types. For instance, you can express the constraints that a generic type T inherits from a class C and conforms to a protocol P as <T where T: C, T: P>.

So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.

like image 30
Brynjar Avatar answered Oct 17 '22 09:10

Brynjar