Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News Swift 5.6 Enhances Type Inference, Introduces Existential Any, and More

Swift 5.6 Enhances Type Inference, Introduces Existential Any, and More

Swift's latest release, Swift 5.6, introduces partial type annotations that work as hints to the type inference engine, disambiguate the syntax for existential types, and improve pointer interaction.

Swift type placeholders aim to ease the burden on the programmer when Swift type inference is not able to derive the type of a particular expression. In such cases, Swift used to require a full type signature. Often, though, the compiler only needs to know the type of a specific subcomponent to be able to infer the rest.

For example, consider the following Swift 5.5 declaration of a function that converts a String into an Double!:

let stringConverter = Double.init as (String) -> Double?

In Swift 5.6, relying on the fact that there is only one Double.init overload that takes a String, it becomes possible to just write:

let stringConverter = Double.init as (String) -> _?

The type placeholder _ is a hint to the compiler to attempt to infer the missing type. This is particularly useful in more complex scenarios, such as when using generics:

enum Either<Left, Right> {
  case left(Left)
  case right(Right)

  init(left: Left) { self = .left(left) }
  init(right: Right) { self = .right(right) }

func makePublisher() -> Some<Complex<Nested<Publisher<Chain<Int>>>>> { ... }

let publisherOrValue = Either(left: makePublisher()) //-- inference error

With Swift 5.6, instead of attempting to provide the full type signature for publisherOrValue, we can use:

let publisherOrValue = Either<_, Int>(left: makePublisher())

Existential any attempts to make Swift syntax less ambiguous and to more clearly distinguish using a protocol name in a generic type constraint or as an existential type. The difference between the two cases is existential types erase their underlying type information and use dynamic dispatch, while generic type constraints store the type information and use static dispatch.

Existential types are used to refer to values of any type that conforms to a given protocol. At the syntax level, you refer an existential type by a protocol name:

protocol AProtocol { ... }

struct AContainer {
    var members: [AProtocol] = []

let valA = AContainer()
// no need to specify what type
// you can append values of differing types to valA.members
// as long as they conform to AProtocol

struct AGenericContainer<T : AProtocol> {
    var members: [T] = []

let valB = AGenericContainer<AType>()
// when instantiating AGenericContainer, you do for a concrete type

Swift 5.6 introduces an alternative syntax for existential types which allows to write:

struct AContainer {
    var members: [any AProtocol] = []

The main advantage of this syntax is it makes clear the different semantics of using existential types and its implications in terms of performance, e.g,. static vs. dynamic dispatch.

Swift 5.6 will support both the new and the old syntax. In a future release, the compiler will emit a warning for every use of an existential type without the any keyword, and in the subsequent major release, the old syntax will become illegal. This change has thus the potential of breaking existing code in the future.

Other significant changes in Swift 5.6 aim to improve working with unsafe pointers, in particular by providing an explicit way to create an uninitialized memory buffer, useful when calling a C API requiring memory into which to store its result; and by allowing to pass a mutable pointer to an API taking an immutable pointer without explicit conversion.

As a final note, Swift 5.6 also extends the Swift Package Manager introducing the ability to define build tool plugins and custom command plugins.

Swift 5.6 is included in Xcode 13.3 and is available experimentally for Amazon Linux 2 and CentOS 7.

About the Author

Rate this Article