Live data from Hacker News

Rust's Block Pattern

notgull.net

111–120 of 121 posts

Re: Rust's Block Pattern

#111
I got so used to taking advantage of this feature in my side projects that my work Kotlin code is now full of “run {}” blocks. Even with a GCed language, it’s very nice to restrict variable lifetimes without needing to split the logic out to its own function.

Re: Rust's Block Pattern

#112
Cute, essentially equivalent to Python's inner functions and Go's closures, e.g in Go:

    func foo(cfg_file string) (parsed, error) {
        config := func() {
            return json.Parse(cfg_file)
        }
        return config.parsed
    }
All of these are however poor solutions to the problem, because they're not true nested functions — they can access arbitrary variables defined outside their scope. Python at least restricts their modification, but Go doesn't. I'm guessing in Rust it's at least explicit in some way?

In any case, the real solution here is to simply allow proper nested functions that behave exactly like freestanding functions in that they can only access what's passed to them:

    func foo(cfg_file string) (parsed, error) {
        func config(cfg_file) config {
            return json.Parse(cfg_file)
        }
        return config.parsed
    }
This way you can actually reason about that block of code in isolation—same effect as when calling a freestanding function, except this doesn't expose the nested function to callers outside the parent function, which is valuable.

Re: Rust's Block Pattern

#113
post #14

Block expression https://doc.rust-lang.org/reference/expressions/block-expr.h... Also in Kotlin, Scala, and nim.

I think this comes from functional programming. I'd just call it "everything is an expression" (which isn't quite true in Rust but it's a lot more true than it is in traditional imperative languages like C++ and Python).

The only things in Rust that are real statements are `let` statements, and item statements (e.g. declaring an `fn` inside a function). All other statements are in fact expressions, although some always return `()` so they're not really useful as such.

Re: Rust's Block Pattern

#114

Earlier quoted context omitted.

I think this comes from functional programming. I'd just call it "everything is an expression" (which isn't quite true in Rust but it's a lot more true than it is in traditional imperative languages like C++ and Python).

The only things in Rust that are real statements are `let` statements, and item statements (e.g. declaring an `fn` inside a function). All other statements are in fact expressions, although some always return `()` so they're not really useful as such.

Surely declaring structs, traits, top-level functions, etc?

Re: Rust's Block Pattern

#115
post #81

> This is why I generally avoid C’s “bottom-up” strategy for organizing code. I think the author misunderstood something....

Yeah, language choice and the way your organise your code seem orthogonal to me

I inferred that they’re referring to the fact that in typical C the compiler must have seen a function earlier in the file for you to use it. One solution (that the author doesn’t like) is to put the leaf functions first so that they’re defined when the compiler sees their callers. The author seems to be ignoring the alternative approach: declaring functions at the top and then writing the in the top-down order that they like.

Re: Rust's Block Pattern

#116
post #94

Earlier quoted context omitted.

For the case where `try` is useful over the functional form (i.e. parent's situation of having a desired Result, plus some unrelated early-returning), that ends up with nested `Result`s though, i.e. spamming an `Ok(Ok(x))` on all the non-erroring cases, which gets ugly fast.

Why couldnt you flatten it?

You have three different value cases (main value, main Err case for `?` to consume, and whatever early-return case). And the `?` operator fully taking up the Err result case means your main-result+early-return values strictly must both be wrapped in an Ok.

Re: Rust's Block Pattern

#117
post #103

Earlier quoted context omitted.

Variable shadowing felt wrong for a while because it's considered verboten in so many other environments. I use it fairly liberally in rust now.

It helps that the specific pattern of redeclaring a variable just to change its mutability for the remainder of its scope is about the least objectionable use of shadowing possible.

That's not the only place I use shadowing though. I use it much more liberally.

For example I feel this is right:

    let x = x.parse()?;

Re: Rust's Block Pattern

#118

I love that this is part of the syntax. I typically use closures to do this in other languages, but the syntax is always so cumbersome. You get the "dog balls" that Douglas Crockford always called them: ``` const config = (() => { const raw_data = ... ... return compiled; })()' const result = config.whatever; // carry on return result; ``` Really wish block were expressions in more languages.

By the by, code blocks on here are denoted by two leading spaces on each line like this

ah, much appreciated!

Re: Rust's Block Pattern

#119

Earlier quoted context omitted.

Wouldn't that also move any referenced variables too? Unlike the block example that would make this code not identical to what it's replacing.

No, unless you ask for it via the `move` keyword in front of the closure. This works fine: https://play.rust-lang.org/?version=stable&mode=debug&editio...

Thanks. I was a genuine question and you answered it well. For some reason I've internalized that closures can capture variables sometimes, but I guess I'm not sure the conditions in which that's true (or perhaps I've learned/mis-remembered the wrong lesson a long time ago.

Re: Rust's Block Pattern

#120

Earlier quoted context omitted.

So many options why oh why. let run with also apply

Each does different things, and Rust also has plenty of them. and_then(), or(), or_else(), then(), the list goes on. Kotlin just implements them more widely. Actually, Kotlin's with() and apply() are more powerful than what Rust can provide. Then again, Rust isn't designed with OO in mind, so you probably shouldn't use those patterns in Rust anyway.

Surely you have to appreciate or else are a different from then examples I gave, the 6 or so kotlin are effectively call a closure with a captured object, with some variation on the return value
Post reply on HN