How to return the runtime Self from a method like createWrapper() -> Wrapper<Self>

I feel like this may be something too obvious that I just can't see at the moment:

class Wrapper<T> {}

class Foo {
    // ❗️ 'Self' is only available in a protocol or as the result of a method in a class; did you mean 'Foo'?
    func createWrapper() -> Wrapper<Self> {
        return Wrapper<Self>()
    }
}

I want this function to be valid for all subclasses of Foo, but how?

You should get a better error on master: covariant 'Self' can only appear at the top level of method result type

like this?

class Wrapper<T> {}

protocol Wrappable {}

extension Wrappable {
    func createWrapper() -> Wrapper<Self> {
        return Wrapper<Self>()
    }
}

class Foo: Wrappable {}

Foo().createWrapper()
2 Likes

Thanks! @ddddxxx

A little hacky, with the empty protocol and all.

Here is some more material to learn from. Self in protocols means one things while Self on classes means something else: Is it just me or did we end up having partly `StaticSelf` in Swift?

1 Like

You can't do this because inside a class, Self is a shorthand for the runtime type of self, and not just an alternate spelling for the name of the class that lexically contains the definition.

If you could write Self there, it would introduce unsoundness into the typing rules. For example, suppose you have a subclass Bar:

class Bar : Foo {}

Now consider this code:

let bar = Bar()
let result1 = bar.createWrapper()

let foo = (bar as Foo)
let result2 = foo.createWrapper()

The type of result1 is Wrapper<Bar>, but the type of result2 is Wrapper<Foo>. These are two unrelated types.

1 Like

I would suggest against using this protocol trick. It's actually equivalent to just declaring a class method returning Wrapper<Foo>. The Self in the protocol is a stand-in for the conforming class, not the runtime type of the self value. So using this idiom just obfuscates the intent of the code in my opinion.

It's not equivalent, because you lose the static type of the subclass. I would even say that this trick is becoming a kind of standard practice. For example, here I use it to observe UIControl events, so that the sender is statically not an abstract base class:

button.onEvent(.touchDown).run { (action: (sender: UIButton, event: UIEvent?)) in }

And here they do about the same using Combine.

Terms of Service

Privacy Policy

Cookie Policy