Initializing multiple variables at once, "Type annotation missing in pattern" error

I'd like to do this:

let x, y, z: UIStackView = {
    let sv = UIStackView()
    sv.axis = .vertical
    sv.distribution = .fillEqually
    return sv
}()

Computer says no. Or more specifically that Type annotation missing in pattern.

I can't figure out how to initialize multiple constants with the same code, except write a function and call that three times. Or initialize them separately, and then run a loop to set the properties. But I wonder if there's a way that's more like the invalid code above.

Doesn’t the closure need to return sv at the end?

No, there isn't.

@CTMacUser sorry I corrected that, copy/paste gone wrong

@xwu Okay, thanks

If you really want to, you could always:

prefix operator <-
prefix func <-<T>(_ v: T) -> (T, T) { (v, v) }
prefix func <-<T>(_ v: T) -> (T, T, T) { (v, v, v) }
prefix func <-<T>(_ v: T) -> (T, T, T, T) { (v, v, v, v) }
// (And so on if/as needed …)

which will enable you to eg:

let (a, b, c, d) = <-"hello"
print(a) // "hello"
print(b) // "hello"
print(c) // "hello"
print(d) // "hello"

and more specifically:

let (a, b, c) = <-{ () -> UIStackView in
  let sv = UIStackView()
  sv.axis = .vertical
  sv.distribution = .fillEqually
  return sv
}()

And if you add this:

typealias X2<T> = (T, T)
typealias X3<T> = (T, T, T)
// (And so on if/as needed …)

The slightly annoying "() -> UIStackView in" can be skipped and you can write:

let (a, b, c) : X3<UIStackView> = <-{
  let sv = UIStackView()
  sv.axis = .vertical
  sv.distribution = .fillEqually
  return sv
}()

Or, you could add this:

protocol Withable: AnyObject {}
extension Withable {
  func with(_ op: (Self) -> Void) -> Self { op(self); return self }
}
extension UIStackView: Withable {}

to be able to just write:

let (a, b, c) = <-UIStackView()
  .with { $0.axis = .vertical }
  .with { $0.distribution = .fillEqually }

But note that, just as in your original example, all three a, b and c will reference the same UIStackView instance, ie:

print(a === b, a === c) // true true

This is probably not what you want.

A minor change to the <- operator (making it take a () -> T closure instead of a T value, execute that and return its result) fixes this issue.

So, the complete solution becomes:

// --- These two general tools ---

protocol Withable: AnyObject {}
extension Withable {
  func with(_ op: (Self) -> Void) -> Self { op(self); return self }
}

prefix operator <-
prefix func <-<T>(_ g: () -> T) -> (T, T) { (g(), g()) }
prefix func <-<T>(_ g: () -> T) -> (T, T, T) { (g(), g(), g()) }
prefix func <-<T>(_ g: () -> T) -> (T, T, T, T) { (g(), g(), g(), g()) }
// (And so on if/as needed …)


// --- Makes it possible to do this ---

extension UIStackView: Withable {}

let (a, b, c) = <-{
  UIStackView()
    .with { $0.axis = .vertical }
    .with { $0.distribution = .fillEqually }
}
1 Like

@Jens That's a fascinating bit of code. I hadn't realized that Swift was so flexible. Thanks for showing :+1:

Terms of Service

Privacy Policy

Cookie Policy