While I can kind of see where you’re coming from I’m not sure about the change;
the key thing about defer is that it doesn’t just execute in the cases you
explicitly define, but can also occur if exceptions are thrown and other exit
cases that could be all over the scope.
To compare with other languages, I’ve used several that achieve this with a
finally block instead, usually as part of a try/catch like so:
try { doSomethingThatCanThrow(); return 1; }
catch (Exception e) { print(e); return 0; }
finally { doSomeCleanup(); }
Here you have two possible exit points, and in both cases the code in the
finally block is executed. But it’s pretty rigid.
This is fine in cases like you suggest where it makes a bit more sense
visually, but the cool thing about Swift is that you can declare defer blocks
all over the place, build upon them and so-on. It means you can group your
cleanup code with the statements that actually require the cleanup, even if
there is a ton of extra code that comes afterwards. For example, opening a TCP
connection may use a defer block right away to ensure the connection is closed
cleanly and any buffers are cleared regardless of how the method ends
(normally, IO error etc.), but before that happens there may be a whole load of
parsing and other operations before you hit the final return statement.
It’s also pretty clear from the keyword defer; putting it after the return
statement actually makes less sense, as there is nothing for it to be deferred
in relation to (as there’s nothing else left to do).
I’d say that if you want cleanup to appear visually afterwards you’d be better
off promising a finally block, this could be nice to have, especially if it
could be applied to most blocks like so:
do {
somethingThatCouldThrow()
if someCondition { return }
} finally { someCleanup() } // Executed whether the block throws,
returns or completes normally
for eachValue in theValues {
if doSomethingTo(eachValue) { break }
if someCondition { return }
else { throw SomeError() }
} finally { doSomeCleanup() } // Executes regardless of whether the
loop ends normally, breaks, returns or throws
And so-on. I’d say that defer is more flexible overall, but there could be some
cause for this visually so you can move simpler deferred code away from the
main method body.
> On 6 Jun 2016, at 20:50, donny wals via swift-evolution
> <[email protected]> wrote:
>
> Hi,
>
> When we’re using defer we write some code that we want to execute the moment
> a scope exits.
> This leads to code that could read like:
>
> let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
> defer { pair = (pair.1, pair.0 + pair.1) }
> return pair.0
> }
>
> What I find strange about this is that we have to write the code that we want
> to execute after the return before the return.
>
> I’d like to propose a change to defer that would allow the above code to be
> written as:
>
> let fibonacci = sequence(state: (0, 1)) { (pair: inout (Int, Int)) -> Int in
> return pair.0
> defer { pair = (pair.1, pair.0 + pair.1) }
> }
>
> This would make the intent of the code more clear (return first, then mutate
> state). Not all cases can benefit from this change, but anytime you exit a
> scope using a return I think it might be more clear to define the defer after
> the return. The code would more closely mirror the intent of the code.
>
> A rule of thumb I’ve come up with for this is that whenever you’re using
> return to exit a scope, any defer in that same scope should be executed
> regardless of it’s position in that same scope. This proposal would
> supplement the way defer currently works.
>
> What do you all think?
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution