Remix.run Logo
mojuba 4 hours ago

I like how Swift solved this: there's a more universal `defer { ... }` block that's executed at the end of a given scope no matter what, and after the `return` statement is evaluated if it's a function scope. As such it has multiple uses, not just for `try ... finally`.

Someone an hour ago | parent | next [-]

I think Swift’s defer (https://docs.swift.org/swift-book/documentation/the-swift-pr...) was inspired by/copied from go (https://go.dev/tour/flowcontrol/12), but they may have taken it from an even earlier language that I’m not aware of.

Defer has two advantages over try…finally: firstly, it doesn’t introduce a nesting level.

Secondly, if you write

       foo
       defer revert_foo
, when scanning the code, it’s easier to verify that you didn’t forget the revert_foo part than when there are many lines between foo and the finally block that calls revert_foo.

A disadvantage is that defer breaks the “statements are logically executed in source code order” convention. I think that’s more than worth it, though.

mojuba an hour ago | parent [-]

Yeah, it's especially handy in UI code where you can have asynchronous operations but want to have a clear start/end indication in the UI:

    busy = true
    Task {
        defer { busy = false }
        // do async stuff, possibly throwing exceptions and whatnot
    }
dwattttt 3 hours ago | parent | prev | next [-]

I was contemplating what it would look like to provide this with a macro in Rust, and of course someone has already done it. It's syntactic sugar for the destructor/RAII approach.

https://docs.rs/defer-rs/latest/defer_rs/

2 hours ago | parent | next [-]
[deleted]
mojuba 2 hours ago | parent | prev [-]

I don't know Rust but, can this `defer` evaluate after the `return` statement is evaluated like in Swift? Because in Swift you can do this:

    func atomic_get_and_inc() -> Int {
        sem.wait()
        defer {
            value += 1
            sem.signal()
        }
        return value
    }
kibwen an hour ago | parent | next [-]

It's easy to demonstrate that destructors run after evaluating `return` in Rust:

    struct PrintOnDrop;
    
    impl Drop for PrintOnDrop {
        fn drop(&mut self) {
            println!("dropped");
        }
    }
    
    fn main() {
        let p = PrintOnDrop;
        return println!("returning");
    }
But the idea of altering the return value of a function from within a `defer` block after a `return` is evaluated is zany. Please never do that, in any language.
ninkendo an hour ago | parent | prev [-]

It gets even better in swift, because you can put the return statement in the defer, creating a sort of named return value:

    func getInt() -> Int {
        let i: Int // declared but not
                   // defined yet!

        defer { return i }

        // all code paths must define i
        // exactly once, or it’s a compiler
        // error
        if foo() {
            i = 0
        } else {
            i = 1
        }

        doOtherStuff()
    }
coldpie a minute ago | parent | next [-]

I would scream if I saw this in an actual codebase.

mojuba an hour ago | parent | prev [-]

Huh, I didn't know about `return` in `defer`, but is it really useful?

troglo-byte 3 hours ago | parent | prev [-]

    #include <iostream>
    #define RemParens_(VA) RemParens__(VA)
    #define RemParens__(VA) RemParens___ VA
    #define RemParens___(...) __VA_ARGS__
    #define DoConcat_(A,B) DoConcat__(A,B)
    #define DoConcat__(A,B) A##B
    #define defer(BODY) struct DoConcat_(Defer,__LINE__) { ~DoConcat_(Defer,__LINE__)() { RemParens_(BODY) } } DoConcat_(_deferrer,__LINE__)

    int main() {
        {
            defer(( std::cout << "Hello World" << std::endl; ));
            std::cout << "This goes first" << std::endl;
        }
    }
rezonant 2 hours ago | parent | next [-]

Why would that be preferable to just using an RAII style scope_exit with a lambda

troglo-byte 2 hours ago | parent [-]

Meh, I was going to use the preprocessor for __LINE__ anyways (to avoid requiring a variable name) so I just made it an "old school lambda." Besides, scope_exit is in C++23 which is still opt-in in most cases.

chuckadams an hour ago | parent | prev | next [-]

"We have syntax macros at home"

2 hours ago | parent | prev [-]
[deleted]