While reading swift forum about exceptions I found interesting issue. One of the examples about exceptions was something like this:
protocol Base {
func foo() throws -> Int
}
protocol Refined: Base {
func foo() -> Int
}
struct Test: Refined {
func foo() -> Int {
0
}
}
It's interesting, I thought it was typo that it would not compile, but it does. I am not sure how this does work behind the scenes. I mean when protocol adopts another protocol it adopts also its requirements. But in this case declaring same method without throws somehow satisfies also first protocol Base.
At the very least I expected Test to need to have 2 implementations of foo. What am I missing here?
This is because a non-throwing function is by definition a sub-type of a throwing function
From the Swift Programming Language book
The throws keyword is part of a function’s type, and nonthrowing functions are subtypes of throwing functions. As a result, you can use a nonthrowing function in the same places as a throwing one.
But you can't do it the other way around so the below code will generate an error
protocol Base {
func foo() -> Int
}
protocol Refined: Base {
func foo() throws -> Int //error: Cannot override non-throwing method with throwing method
}
Also note that this is not only for protocols, if you remove the func declaration from Refined you can still implement the function in Test as non throwing.
Related
I would like to extend AsyncSequence in ways that depend on whether the sequence can throw. Neither AsyncSequence nor AsyncIteratorProtocol distinguish such sequences explicitly. Yet, the concurrency module does come with concrete sequences with throwing and non-throwing variants. The only generic difference I see is that the next method of non-throwing sequences are rethrowing. Here is an example:
extension AsyncMapSequence : AsyncSequence {
struct Iterator : AsyncIteratorProtocol {
mutating func next() async rethrows -> Transformed?
}
}
Whereas the throwing variant is a plain throws:
extension AsyncThrowingMapSequence : AsyncSequence {
struct Iterator : AsyncIteratorProtocol {
mutating func next() async throws -> Transformed?
}
}
(I am not even sure how rethrows is even possible for a method that does not take any arguments. The only thing that comes to mind is that the curried expression of such a method could throw some light on that...)
So, the question is how to express something along the lines of:
extension AsyncSequence where AsyncIterator /* is not throwing */ {
}
When this proposal is fully implemented you will be able to express conformance to a failable sequence by providing some syntax sugar
extension AsyncSequence where nothrow AsyncIterator {
or
struct Foo<S: nothrow AsyncSequence>
This comes from the #rethrows annotation currently attached to AsyncSequence
#rethrows public protocol AsyncSequence
Let's assume I have protocol FooProtocol in Swift with one method with its default implementation:
protocol FooProtocol {
func foo()
}
extension FooProtocol {
func foo() {
print("protocol")
}
}
and class FooClass with cast instance:
class FooClass : FooProtocol {
func foo() {
print("class")
}
}
let A = FooClass() as FooProtocol
A.foo()
As expected, "class" will be printed in console.
However, if I make foo() method of my protocol being optional - "protocol" will be printed instead.
#objc protocol FooProtocol {
#objc optional func foo()
}
But if I call A.foo?() rather than A.foo() - "class" will be printed again.
I wonder, just for theoretical purposes, what the hell is happening here? Any explanations are appreciated.
You cannot call optional protocol method without optional chaining. I.e. if you comment out
extension FooProtocol {
func foo() {
...
and you try to call A.foo(), you will get a compilation error:
value of optional type '(() -> ())?' must be unwrapped to a value of type '() -> ()'
As you see the signature of optional is not even the same as non-optional. So by calling A.foo(), you are not calling method you implemented, you are calling default protocol implementation directly.
It also makes sense since primary reason for #objc optional is interpolation with Objective C, where default protocol implementation won't be visible. So relying on protocol function for objc function would produce different result on swift and objective c, which is undesired.
I cannot say if there's any loophole to use both optional and default protocol implementation on the same function. But question is: do you really need it?
If you are concerned with interpolation, you need equal solution for both Swift and Objective c, and that means you rather need basic protocol implementation, which both Swift and objc can inherit
If you need no interpolation, you don't really need optional, and can rely on default protocol implementation alone.
After converting from Swift 2.2 to 3.0 my Array extension does not compile anymore, because it contains a call to global standard library function min<T>(T,T) and shows compiler error extra argument in call.
Here's a simple way to reproduce the error:
extension Array {
func smallestInt(first: Int, second: Int) -> Int {
return min(first, second) // compiler error: "Extra argument in call"
}
}
I get the same error when adding the same function to an extension of Dictionary, while the exact same code compiles just fine in an extension of other types (e.g. String or AudioBuffer):
Looking at the documentation of Array and Dictionary, I find that there are instance methods on Sequence named public func min() -> Element? and public func min(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?. While both String and AudioBuffer do not have any kind of min(...) function.
Is it possible that this is the reason why I can't call the global function? The compiler can't distinguish between global func min<T>(T,T) and self.min(...) although they have completely different signatures?
Is this a bug or a feature? What am I doing wrong? How can I call min(T,T) correctly inside an Array extension?
I see no reason why the compiler shouldn't be able to resolve this function call, therefore I would consider it a bug (it has already been filed – see SR-2450).
It seems to occur whenever attempting to call a top-level function with the same name, but unambiguously different signature to a method or property that's accessible from the same scope in a given type (instance or static).
An even simpler example would be:
func foo(_ a: Int) {}
struct Foo {
func foo() {} // or static func foo() {}, var foo = 0, static var foo = 0
func bar() {
foo(2) // error: argument passed to call that takes no arguments
}
}
Until fixed, a simple solution would be to prefix the call with the name of the module in which it resides in order to disambiguate that you're referring to the top-level function, rather than the instance one. For the standard library, that's Swift:
extension Array {
func smallestInt(first: Int, second: Int) -> Int {
return Swift.min(first, second)
}
}
In Swift 4, the compiler has a better diagnostic for this error (though the fact that it's still an error is a bug IMO):
extension Array {
func smallestInt(first: Int, second: Int) -> Int {
// Use of 'min' refers to instance method 'min(by:)'
// rather than global function 'min' in module 'Swift'
// - Use 'Swift.' to reference the global function in module 'Swift'
return min(first, second)
}
}
Although what's interesting is that the compiler will now also warn on attempting to call a standard library method with the same name as a stdlib top-level function:
extension Array where Element : Comparable {
func smallest() -> Element? {
// Use of 'min' treated as a reference to instance method in protocol 'Sequence'
// - Use 'self.' to silence this warning
// - Use 'Swift.' to reference the global function
return min()
}
}
In this case, as the warning says, you can silence it by using an explicit self.:
extension Array where Element : Comparable {
func smallest() -> Element? {
return self.min()
}
}
Although what's really curious about this warning is it doesn't appear to extend to non-stdlib defined functions:
func foo(_ a: Int) {}
struct Foo {
func foo() {}
func bar() {
foo() // no warning...
}
}
Say I have the following code:
protocol A {}
struct B: A {}
func b() -> A {
return B()
}
func f<T where T: A>(t: T) -> T {
return t
}
f(b())
This results in the following error (Xcode 6.3 beta 3):
Playground execution failed: Test.playground:12:1: error: generic parameter 'T' cannot be bound to non-#objc protocol type 'A'
f(b())
^
Test.playground:8:6: note: in call to function 'f'
func f<T where T: A>(t: T) -> T {
^
I feel like this is a shortcoming with the implementation of generics in Swift. Is there a way to nicely work around this issue while still keeping the generics?
In my code f() has additional type requirements; I could just forget about protocols and generics altogether, but I don't accept defeat that easy ;-).
Implicite down casting might not be allowed here, you need to force it in the implementation of method b() :
func b () -> A {
return B() as A
}
If you mark the protocol with #objc and change the struct to a class it will work, because then you will be in Objective C compatible territory for which different rules exist.
The reason is that in Swift protocols are not considered to be concrete implementations of themselves due to possible static requirements of the protocol and/or associated types. In my point of view this is indeed a (very annoying) shortcoming of Swift. See also issue:
Protocol doesn't conform to itself?
I think it might be just a rare situation, but how can I deal with the same (ambiguous) function from two different protocol. For example, I have these defines:
protocol A {
func foo()
func bar() -> Int
}
protocol B {
func foo()
func bar() -> String
}
Now I have a class conforms A and B. Can I implement different versions of foo() for A and B separately. If I can do so, how can I call them?
P.S. I know for bar(), I can use something like this to make a call:
let anInt = (instance as A).bar()
let aString = (instance as B).bar()
Is it possible to do similar thing on foo() function?
No. The point of a protocol is that it requires that an object provide a particular method, but the method is not "tied" to the protocol. That's why you can use extensions to cause existing classes to conform to new protocols using their existing methods.
As a note, this sounds like a mistake in the protocol design. Having two protocols require different semantics for the same method name suggests that the method is incorrectly named.