Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I think your confusing Swift's protocols with something akin to Java's interfaces. I don't really know Swift, but it looks a lot like Rust, so I'm going to assume that they work the same.

In Java, the following function definition compiles just fine:

  IShape getShape() {
      return new Rectangle(20, 40);
  }
And, assuming that `IShape` has a `draw()` method, you can write:

  IShape shape = getShape();
  shape.draw();
It works because there is dynamic dispatch occurring at runtime: the JVM will look for the implementation of `draw()` in the `Rectangle` class (not sure of the exact mechanism, but that's the idea), and call it. To find it, the value (here, shape) must holds a reference either to its class so the JVM can go and look for the implementation, or to a table of all methods it implements (I think it's the first). So the compiler code doesn't know the layout of the concrete type used, it just add instructions to go look for the implementation at runtime. But that's fine, because any Object in Java is in fact like that: a fat pointer, containing a pointer to the data, and a pointer to the implementations.

I won't work in Swift because, as far as I know, there is no dynamic dispatch, at least by default. When you write the following:

  func render<T: Shape>(_ shape: T, at point: Point) { … }
The compiler will know what is the concrete type of `T`, and so will use its implementation of the `draw()` method. It won't be found at runtime, it is known at compile time. What this means is that a protocol is not a type. This is the important part. An interface in Java is a type, because it doesn't dictate how the method is called, it allows the concrete type to live under the hood, and call the right method at runtime. A class in Swift is a type because you know how to call it, how to access it. But a protocol is just a set of constraints on a type, not a type by itself.

That's why you need the `some` in return position. Well, you don't really need the syntax, but it helps understanding the difference with the "same" Java code. The `some` keyword says that the function will returns some type that will implements the `Shape` protocol. It will in fact return the concrete type, but this is not included in the type signature, so it can change, it can hide implementation details, without making a breaking change in the API. It also means that the following won't compile (I'm not sure here, but it works that way in Rust):

  func union(_ leftShape: some Shape, _ rightShape: some Shape) -> some Shape {
      if isEmptyShape(leftShape) {
          return rightShape
      } else if isEmptyShape(rightShape) {
          return leftShape
      }
      return Union(leftShape, rightShape) 
  }
Because here, all code path don't return the same concrete type. If you want different concrete types, you'll need the other keyword, `any`. `any` is in fact a lot like the interfaces in Java, because it uses dynamic dispatch under the hood (if it works like it does in Rust). The compiler will know how to turn the concrete type into the dynamically dispatched one.


Generic functions in Swift are compiled separately from their callers. Protocol requirements called on values of generic parameter type are dynamically dispatched via a witness table passed in under the hood for each generic requirement.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: