How to assert static array is exactly n element?

struct Foo {
    static private let levelColors = [
        Color(red: 123 / 255, green: 176 / 255, blue: 140 / 255),
        Color(red: 123 / 255, green: 176 / 255, blue: 140 / 255),
        Color(red: 123 / 255, green: 176 / 255, blue: 140 / 255),
        Color(red: 123 / 255, green: 176 / 255, blue: 140 / 255),
        Color(red: 123 / 255, green: 176 / 255, blue: 140 / 255),
        Color(red: 123 / 255, green: 176 / 255, blue: 140 / 255),
        Color(red: 247 / 255, green: 218 / 255, blue: 185 / 255),
        Color(red: 247 / 255, green: 218 / 255, blue: 185 / 255),
        Color(red: 232 / 255, green: 170 / 255, blue: 152 / 255),
        Color(red: 222 / 255, green:  92 / 255, blue:  67 / 255),
    ]

    // how to assert this array is exactly 10 elements?
}

The flexibility you have here depends on whether you want this to fail at compile time or at runtime.

If you want it to fail at compile time, then without variadic generics (and probably integer-valued generic arguments like C++ has), you'd have to write a function that takes exactly 10 arguments and returns them in an array:

func array10<E>(
  _ e0: E, _ e1: E, _ e2: E, _ e3: E, _ e4: E,
  _ e5: E, _ e6: E, _ e7: E, _ e8: E, _ e9: E
) -> [E] {
  return [e0, e1, e2, e3, e4, e5, e6, e7, e8, e9]
}

struct Foo_CompileChecked {
  static let elements = array10(
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  )
}

But you'd have to do that for every possible number that comes up in your code. If you need to support various counts and don't want to write them all from scratch, then the best you can do right now is something that will fail at runtime the first time the array is accessed. You could wrap the array initialization in a closure that is immediately evaluated, but which also asserts the array's count:

struct Foo_Closure {
  static let elements: [Int] = {
    let elements = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    assert(elements.count == 10)
    return elements
  }()
}

This could be simplified with a helper function:

func assertArray<Element>(_ array: [Element], hasCount count: Int) -> [Element] {
  assert(array.count == count)
  return array
}

struct Foo_HelperFunction {
  static let elements = assertArray(
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    hasCount: 10)
}

In -O builds, the optimizer does a good job of making the extra code go away and it looks like they all optimize down to the same thing a raw array literal would: https://godbolt.org/z/pVa_Fo

1 Like