Protocol Func Returning Self

Protocol func returning Self

The problem is that you're making a promise that the compiler can't prove you'll keep.

So you created this promise: Calling copy() will return its own type, fully initialized.

But then you implemented copy() this way:

func copy() -> Self {
return C()
}

Now I'm a subclass that doesn't override copy(). And I return a C, not a fully-initialized Self (which I promised). So that's no good. How about:

func copy() -> Self {
return Self()
}

Well, that won't compile, but even if it did, it'd be no good. The subclass may have no trivial constructor, so D() might not even be legal. (Though see below.)

OK, well how about:

func copy() -> C {
return C()
}

Yes, but that doesn't return Self. It returns C. You're still not keeping your promise.

"But ObjC can do it!" Well, sort of. Mostly because it doesn't care if you keep your promise the way Swift does. If you fail to implement copyWithZone: in the subclass, you may fail to fully initialize your object. The compiler won't even warn you that you've done that.

"But most everything in ObjC can be translated to Swift, and ObjC has NSCopying." Yes it does, and here's how it's defined:

func copy() -> AnyObject!

So you can do the same (there's no reason for the ! here):

protocol Copyable {
func copy() -> AnyObject
}

That says "I'm not promising anything about what you get back." You could also say:

protocol Copyable {
func copy() -> Copyable
}

That's a promise you can make.

But we can think about C++ for a little while and remember that there's a promise we can make. We can promise that we and all our subclasses will implement specific kinds of initializers, and Swift will enforce that (and so can prove we're telling the truth):

protocol Copyable {
init(copy: Self)
}

class C : Copyable {
required init(copy: C) {
// Perform your copying here.
}
}

And that is how you should perform copies.

We can take this one step further, but it uses dynamicType, and I haven't tested it extensively to make sure that is always what we want, but it should be correct:

protocol Copyable {
func copy() -> Self
init(copy: Self)
}

class C : Copyable {
func copy() -> Self {
return self.dynamicType(copy: self)
}

required init(copy: C) {
// Perform your copying here.
}
}

Here we promise that there is an initializer that performs copies for us, and then we can at runtime determine which one to call, giving us the method syntax you were looking for.

How to use Self for method return types in Swift?

With such a hierarchy, you would have to make the class conforming to the protocol final:

protocol Marker {
func copy() -> Self
}
final class Marker1 : Marker {
func copy() -> Marker1 {
return Marker1()
}
}

The final is needed because when you don't apply final and you create a subclass Marker2: Marker1 then copy would not return the correct class Self any more.

You can workaround that by creating a required initializer and always create the correct instance:

protocol Marker {
init()
func copy() -> Self
}
class Marker1 : Marker {
required init() {
}
func copy() -> Self {
let copy = type(of: self).init()
return copy
}
}

(original code removed because does not work)

Related: Implementing copy() in Swift

Swift protocol defining class method returning self

Self in a protocol is a requirement that implementations of the protocol use their own type. Since Invoice is the type you're adopting the protocol in, your implementation of FromJson should have a return type of Invoice.

Why using Self as return type is not considered as the protocol's constraint?

Because it's not useful to have a Self as a parameter type of a method. Suppose that you can do:

protocol P {
func f(_ x: Self)
}

class A: P {
func f(_ x: Self) {
// not relevant
}
}

class B: A { }

Now suppose I have:

func g(x: A) {
// what can I do with x?
}

The fact is, there is no way to call x.f. Because I can pass an instance of B to x, in which case x.f would accept a B instead. x could be an instance of any subclass of A that I have no way of knowing at compile time, so I don't know what I can pass to x.f.


Compare that to Self used as the return type:

protocol P {
func f() -> Self
}

// Implementation:
class A: P {
func f() -> Self {
self
}
}

class B: A { }

func g(x: A) {
let foo: A = x.f()
}

Here, we know that I can at least assign the return value of x.f to a variable of type A. Even if x is an instance of B, which means that f returns a B, we can still assign that to a variable of type A.

Conforming to a static function that returns Self

Self in a protocol is a requirement that conformance of the protocol use their own type. So you need to change Self to NameData in the return type of the method when you conform this in your class extension.

extension NameData: NSItemProviderReading {
static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> NameData {
return NameData(name: "Test")
}
}

And one more thing, you need to make your NameData class final.

How is Self in protocol interpreted in subclass?

According to the documentation Self in this case will be A since A is the one conforming to the protocol, B is only doing it indirectly as a subclass of A.

So when A conforms to Copyable you are saying that A and all its subclasses must have an init(_ instance: A)

In a protocol declaration or a protocol member declaration, the Self type refers to the eventual type that conforms to the protocol.

You can actually test this by removing the required init(_ instance: A) init and you will get an error even if you have an init(_ instance: B), so since A is the class conforming to the protocol you must have an init where the instance argument is A

returning swift protocol associated type in multiple methods

The solution you came up with by playing around is exactly what you need

As mentioned elsewhere, the main issue with your first protocol is that you're enforcing createSomeView() createAnotherView() both return the same type. While ViewA() and ViewB() are both candidates for V, since they conform to View they are still different types, and therefore cannot BOTH be V in a given object.

By defining both V1 and V2, you allow for each function to return a different type, or the same type, it's all acceptable. By making both V1 and V2 require View conformance, you allow for the some View syntax



Related Topics



Leave a reply



Submit