What's `Any` in Swift on earth?

2021-03-07
摘要: How does Swift know the address of an element in an array of Any? This raises another question: what's `Any` in Swift on earth?

Usually, in other programming languages like C/C++, we can only declare an array of a specified type, therefore we can calculate the address of an element by: start + index * Element.size .

However, in Swift, we can have an array of Any type:

var array: [Any] = [0, "1"] // an array of Any type. For example, this array contains an Int value and a String value.
print(array[1]) // print “1”

So how to calculate the address of an element of array with a specified index if we don’t have a fixed size of elements in the array? What’s the magic in Any type?

One possible guess is Any encapsulates the original content via a pointer, so we can store Any type in a fixed size. Actually, it really does something alike.

https://forums.swift.org/t/how-does-swift-know-the-address-of-an-element-in-an-array-of-any/45415

Alejandro Alonso:

The type erased Any has a fixed layout of size 32 on 64b and 16 on 32b which contains enough information to recover the original type and value. If I were to write this out in C/C++:

struct Any {
  size_t data[3];
  const void *type;
}

So in this case, an array of Any is simply an array of that structure as it’s single element type. Swift’s print does some reflection when it encounters this Any type to print the actual value within it. Also, keep in mind that array’s elements may not always be contiguous, so if you want to access its contiguous memory I would suggest using: withContiguousStorageIfAvailable.

And in the type layout part of ABI documentation of Swift, we can find more information.

Any type are laid out using Existential Containers, more specifically, using Opaque Existential Containers. It does this using a fixed-size buffer, which is three pointers in size and pointer-aligned. This either directly contains the value, if its size and alignment are both less than or equal to the fixed-size buffer’s, or contains a pointer to a side allocation owned by the existential container. The type of the contained value is identified by its type metadata record, and witness tables for all of the required protocol conformances are included. The layout is as if declared in the following C struct:

struct OpaqueExistentialContainer {
  void *fixedSizeBuffer[3];
  Metadata *type;
  WitnessTable *witnessTables[NUM_WITNESS_TABLES];
};

And you can find more information about Size, Stride and Alignment in Swift Here.