How to make `ExpressibleByStringLiteral` init either failable somehow or throws?

struct Foo: ExpressibleByStringLiteral {
    init(stringLiteral value: String) /* throws :( not allow? */ {
        guard value == "good" else {
            // how to fail?
            // neither failable or throws seem to be allowed?
            fatalError("Ha? what to do here, want to fail somehow, not crash!!!")
        }
        // okay, we are good ...
    }
}

basically Foo init by string literal only works for some string values, else it needs to fail somehow.

1 Like

fatalError is the best you can do for now. Hopefully optimizer improvements will eventually allow literals to be evaluated as compileā€time constants, which would elevate the fatalError to an outright build failure.

2 Likes

How would a throwing assignment work? Where should you put try?

do {
  // here?
  try let foo: Foo = "good"
  // or maybe here?
  let foo: Foo = try "good"
} catch { }

I don't see a workable solution from the syntactic standpoint alone.


A solution that may work for your use case: mimicking a failable initializer.

public struct Foo {}

extension Optional: ExpressibleByStringLiteral,
                    ExpressibleByUnicodeScalarLiteral,
                    ExpressibleByExtendedGraphemeClusterLiteral where Wrapped == Foo {
  public init(stringLiteral value: String) {
    guard value == "good" else {
      self = nil
      return
    }
    // okay, we are good ...
    self = Foo()
  }
  
  public init(unicodeScalarLiteral value: String) {
    self = .init(stringLiteral: value)
  }
  
  public init(extendedGraphemeClusterLiteral value: String) {
    self = .init(stringLiteral: value)
  }
}
let foo: Foo? = "bad"   // assigns nil
let bar: Foo? = "good"  // assigns Foo()
2 Likes

One thing you could do is make Optional<Foo> conform to the Expressible protocol, rather than Foo itself.

3 Likes