Losiowaty
(Pawel Lopusinski)
1
Hey everyone!
I've got a question for a situation related to this : Concerned about this protocol extension/conformance bug
To set the scene - normally we cannot have a property and a function with the same name (or two properties with the same name), but from above post we know that it's possible to have this via a "clever" use of protocols and protocol extensions.
So now let's take this code :
protocol P1 {
func foo()
}
extension P1 {
func foo() {
print("P1.foo")
}
}
protocol P2 {
var foo: () -> Void { get }
}
extension P2 {
var foo: () -> Void {
{
print("P2.foo")
}
}
}
struct Overloaded: P1, P2 {
var foo: Int = 0
}
let overLoaded = Overloaded()
overLoaded.foo // #1
overLoaded.foo() // #2
foo is three things :
- a property on
Overloaded
- a function on
P1
- a closure on
P2
So while behaviour for #1 is obvious - we directly access the property on Overloaded struct - I was surprised that #2 didn't throw an "Ambiguous use of 'foo'" kind of error.
In my tests it always called the closure on P2 resulting in printing "P2.foo".
I know I can force P1.foo to be called by doing (overLoaded as P1).foo().
My question is - is this behaviour guaranteed and documented? Will Swift always choose a property / getter instead of a func when both are available?
1 Like
It could also be an unapplied method reference for P1, or a call to the getter on P2. This one is actually the most ambiguous of the three, barring type context.
1 Like
Losiowaty
(Pawel Lopusinski)
3
Checked one more thing that seems to confirm Swifts preference for using a variable first instead of a function.
If we modify P2 to have a property of a @dynamicCallable / callAsFunction type it also uses it over func P1.foo()
Full code :
struct Callable {
func callAsFunction() {
print("Callable.callAsFunction")
}
}
protocol P1 {
func foo()
}
extension P1 {
func foo() {
print("P1.foo")
}
}
protocol P2 {
var foo: Callable { get }
}
extension P2 {
var foo: Callable { .init() }
}
struct Overloaded: P1, P2 {
var foo: Int = 0
}
let overLoaded = Overloaded()
overLoaded.foo() // P2.foo.callAsFunction() called here
Still - very much interested in getting confirmation that this is indeed guaranteed behaviour.
1 Like