Following up on the topic of result builders, and also on unused results:
A number of comments on here asked about result builders. SE-0380 explicitly subsetted out result builders, since in those contexts if
statements were already expressions, but of a different form (they build a kind of Either). The same would be true of this proposal – result builders put the compiler into a very different mode where there is already handling of multi-statement bodies without returns. I should have included this in the proposal.
So this code would remain unchanged:
struct V: View {
// returns TupleView<(Text, Text)>
var body: some View {
Text("Hello")
Text("World")
}
}
This works this way, in today's Swift and under this proposal, because View.body
is a @ViewBuilder
.
As others have pointed out though, if you define similar but non-ViewBuilder function, it behaves differently. Today:
// Error: Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
var standalone: some View {
Text("Hello")
Text("World")
}
Under this proposal, this would compile, but importantly, generate a warning:
var standalone: some View {
Text("Hello") // Warning: Result of 'Text' initializer is unused
Text("World")
}
This is essentially a variant of many other examples in this thread, where the concern is that a user places a new line after what was previously a return value:
// Constant 'carBackDoorLabel' inferred to have type '()',
// which may be unexpected
let carBackDoorLabel = switch carBackDoorButtonAction {
case .lock:
"Locked" // Warning: String literal is unused
print("childSafety: \(childSafety)")
case .unlock:
if childSafety {
"Locked" // Warning: String literal is unused
} else {
"Unlocked" // String literal is unused
}
print("childSafety: \(childSafety)")
}
So this code would compile (though in all likelihood, it would fail later due to carBackDoorLabel
being of the wrong type) but generate new warnings indicating the mistake that was made.
One option here is to add logic that, when implicit return (or a multi-statement if
branch) is used, and the scope contains an unused value, then this would generate an error, not a warning. This would make the error impossible to ignore (it could also be customized to explain why it is an error i.e. "did you mean to return or store this value?"
The exception here is where @discardableResult
is involved. This particular issue might be fairly niche. A step further might be to ignore result discard ability when using implicit return (given discardable results are just sugar that avoids a _ =
this is basically sugar vs sugar – you can silence the warning with an explicit return
, or with a _ =
).
Of course, this does not address the "this harms readability even when correct" concern – but does seem to address the "refactoring would be more risky" issue to some extent.