Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Self for method return types in Swift?

This code produces Xcode error messages that lead you in a circle. Let's say I have a protocol called Marker and I'd like Markers to copy themselves. Here's a first guess...

protocol Marker {
    func copy() -> Self
}
class Marker1 : Marker {
    func copy() -> Self {
        return Marker1()   // error here
    }
}

(I'm not sure how to use Self correctly, because I can't find it in the The Swift Programming Language document. If you know where it's documented, please include that in answer.)

That code gives an error on the marked line: Cannot convert return expression of type 'Marker1' to return type 'Self' and it suggests a fix: Insert ' as! Self'.

I accept the fix:

...
    return Marker1() as! Self
...

This leads to another compiler error: 'Self' is only available in a protocol or as the result of a method in a class; did you mean 'Marker1'?

If I accept that "fix" it goes back to the original error. I'd call that a bug in Xcode. Let's try something else:

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

Another error: Method 'copy()' in non-final class 'Marker1' must return `Self` to conform to protocol 'Marker'

Making the class final does fix the error. But is there a way to do this without making a class final? And where is Self documented?

like image 899
Rob N Avatar asked Oct 20 '25 06:10

Rob N


1 Answers

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

like image 162
Sulthan Avatar answered Oct 21 '25 20:10

Sulthan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!