Error information could be preserved...
Using your code with unchecked exceptions
typealias A = Int
typealias B = Int
typealias C = Int
struct XXX {
var a: A
var b: B
var c: C
}
struct XXXBuilder {
var a: A?
var b: B?
var c: C?
func build() -> XXX {
XXX(
a: a.unwrap(),
b: b.unwrap(), // line 22
c: c.unwrap()
)
}
}
let xxx = dynamicTry {
XXXBuilder(a: 1, b: nil, c: 3).build()
} catch: { error in
print(error)
}
print(xxx)
produces this output:
correctly pinpointing the nature of the error ("OptionalUnwrapError.unwrappingNil") and its exact location ("App/main.swift", 22):
XXX(
a: a.unwrap(),
b: b.unwrap(), // line 22
c: c.unwrap()
)
It is true that if we "upgrade" the vanilla "!" to use unchecked exceptions machinery and don't make any changes to the language itself you won't be able adding a descriptive comment about the error:
let y = x! // no comment / custom error here
x!.mutatingFunc() // β
L-value usage possible
let y = x.unwrap(comment: "x") // β
comment / custom error possible
x.unwrap(comment: "x").mutatingFunc() // π L-value usage impossible
If both L-value usage and a comment (or a customised error) is needed we'd need a language change, e.g.:
let y = x! // β
let z = x!(comment: "x") // β
x!.mutatingFunc() // β
x!(comment: "x").mutatingFunc() // β
Correction / Addition:
We'd be able abusing subscripts to add a descriptive comment / customised error:
let y = x! // β
let z = x[] // β
let t = x[comment: "x"] // β
x!.mutatingFunc() // β
y[].mutatingFunc() // β
t[comment: "x"].mutatingFunc() // β
The code sketch
protocol HasDefaultValue {
static var defaultValue: Self { get }
}
extension HasDefaultValue where Self: Inittable {
static var defaultValue: Self { .init() }
}
extension Optional where Wrapped: HasDefaultValue {
// TODO: make some parameters autoclosures for efficiency
subscript(comment comment: String = "", file: String = #fileID, line: Int = #line) -> Wrapped {
get {
guard let self else {
print("record error, file: \(file), line: \(line), comment: \(comment)")
return Wrapped.defaultValue
}
return self
}
set {
self = newValue
}
}
}
struct SS: Inittable, HasDefaultValue {
mutating func mutatingFunc() {}
}
var xx: SS?
xx[comment: ""].mutatingFunc()
Is it possible using a "subscript" approach with normal try/catch instead of the above unchecked exceptions? To some extent only:
As with pretty much any feature there are multiple aspects at hand here, and multiple interpretations of what should be "a goal" and "non goal". Let me write a few:
- the feature should not require language / compiler change
- the feature shall use the standard try / catch mechanism
- the feature should allow adding a descriptive comment (or allow to specify a customised error)
- the feature shall allow identifying the error source
- the feature should be a "safe" version of
"!" usable in all contexts (including L-value).
I noticed that other people value #1 most and #5 least, and personally I'd choose the priorities the opposite way around: #5 on my book is the most important and #2 & #3 (and even #1 if needed) could be sacrificed.