[HN Gopher] Rust's Block Pattern
       ___________________________________________________________________
        
       Rust's Block Pattern
        
       Author : zdw
       Score  : 209 points
       Date   : 2025-12-19 04:56 UTC (1 days ago)
        
 (HTM) web link (notgull.net)
 (TXT) w3m dump (notgull.net)
        
       | nadinengland wrote:
       | 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.
        
         | charleszw wrote:
         | Yes, I constantly use this pattern in C++/JavaScript, although
         | I haven't tested how performant it is in the former (what does
         | the compiler even do with such an expression?)
        
           | paavohtl wrote:
           | At least in simple cases the compiler will just inline the
           | closure, as if it never existed. There shouldn't be any
           | measurable overhead.
        
         | dwattttt wrote:
         | By the by, code blocks on here are denoted by two leading
         | spaces on each line                 like       this
        
         | notpushkin wrote:
         | Interesting that you can use blocks in JS:                 {
         | const x = 5;         x + 5       }       // => 10       x
         | // => undefined
         | 
         | But I don't see a way to get the result out of it. As soon as
         | you try to use it in an expression, it will treat it as an
         | object and fail to parse.
        
         | pwdisswordfishy wrote:
         | https://github.com/tc39/proposal-do-expressions
         | 
         | (Not to be confused with do notation)
        
       | aabdelhafez wrote:
       | It's idiomatic in Kotlin as well!
       | 
       | https://kotlinlang.org/docs/scope-functions.html
        
         | simon_void wrote:
         | I agree, i started with (scope) blocks in Rust, but keep the
         | habit in Kotlin win the run - scope-function. Since run takes
         | no arguments, it feels like the closest equivalent to Rust
         | scopes (compared to other Korlin scope functions, which also
         | keep their local variables from polluting the rest of the
         | function body).
        
         | zaphirplane wrote:
         | So many options why oh why. let run with also apply
        
           | jeroenhd wrote:
           | 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.
        
             | saghm wrote:
             | I think you've misunderstood the point they were making by
             | addressing the number as if it was the only concern and
             | then only mentioning the actual point they were trying to
             | make as if it were an incidental afterthought. I don't
             | think it's likely they're criticizing five functions in the
             | standard library is too many, but that having five special
             | functions with certain semantics that only apply to them is
             | too many. The methods you mention in Rust are all in the
             | first category; you could easily write them yourself for
             | any type you define without needing to resort to wrapping
             | any of them. It's not clear to me that someone could write
             | a function in Kotlin with special scoping semantics around
             | an object without resorting to wrapping one of those
             | functions.
        
               | jeroenhd wrote:
               | The Kotlin functions are actually quite easy to write,
               | they're all written in standard Kotlin.
               | 
               | also: https://github.com/JetBrains/kotlin/blob/2.3.0/libr
               | aries/std...
               | 
               | apply: https://github.com/JetBrains/kotlin/blob/2.3.0/lib
               | raries/std...
               | 
               | let: https://github.com/JetBrains/kotlin/blob/2.3.0/libra
               | ries/std...
               | 
               | with: https://github.com/JetBrains/kotlin/blob/2.3.0/libr
               | aries/std...
               | 
               | run (two overloads): https://github.com/JetBrains/kotlin/
               | blob/2.3.0/libraries/std... and https://github.com/JetBra
               | ins/kotlin/blob/2.3.0/libraries/std...
               | 
               | These all heavily rely on Kotlin's ability to write an
               | extension function for any class. When you write `with(x)
               | { something() }` you're extending the type of `x` (be
               | that int, List<String>, or SomeObject) with an anonymous
               | method, and passing that as a second parameter.
               | 
               | Consider the signature here:                   public
               | inline fun <T, R> with(receiver: T, block: T.() -> R): R
               | 
               | The first object is a generic object T, which can be
               | anything. The second is a member function of T that
               | returns R, which again can be just about anything, as
               | long as it operates on T and returns R.
               | 
               | Let does it kind of diferently:                   public
               | inline fun <T, R> T.let(block: (T) -> R): R
               | 
               | This is an extension method that applies to _every single
               | class_ as T isn 't restricted, so as long as this
               | function is in scope (it's in the standard library so it
               | will be), every single object will have a let() method.
               | The only parameter, block, is a lambda that takes T and
               | returns R.
               | 
               | So for instance:                  val x = makeFoo()
               | with (x) {           bar = 4        }
               | 
               | is syntactic sugar for something like:
               | fun Foo.anonymous() {           this.bar = 4        }
               | val x = makeFoo()        with(x, Foo::anonymous)
               | 
               | You could absolutely write any of these yourself. For
               | instance, consider this quick example I threw together:
               | https://pl.kotl.in/S-pHgvxlX
               | 
               | The type inference is doing a lot of heavy lifting, i.e.
               | taking a lambda and automatically turning it into an
               | anonymous extension function, but it's nothing that you
               | cannot do yourself. In fact, a wide range of libraries
               | write what might look like macros in Kotlin by leveraging
               | this and the fact you can define your own inline
               | operators (i.e. https://pl.kotl.in/TZB0zA1Jr).
               | 
               | This isn't possible in many other languages because
               | taking a generic type definition and letting it possibly
               | apply to every single existing type is not exactly
               | popular. Combined with Kotlin's ability to extend
               | nullable types (i.e. this = null) as well makes for a
               | language system that wouldn't work in many other flexible
               | languages.
        
               | saghm wrote:
               | Fair enough, I retract my previous comment. Unfortunately
               | there seem to a lot of pieces that are unfamiliar here so
               | I'm not really able to understand parts of this but I
               | trust that you understood what I was saying well enough
               | to know that it was wrong.
        
       | lenkite wrote:
       | The first example given is not at all convincing. Its is clear as
       | the sky that loading the config file should be be a separate
       | function of its own. Coupling sending HTTP requests with it makes
       | no sense.
       | 
       | The second example "erasure of mutability" makes more sense. But
       | this effectively makes it a Rust-specific pattern.
        
         | dtdynasty wrote:
         | It's essentially an inline function with only 1 client. Can be
         | a preference for inline readability and automatically enforces
         | there are no other clients of the "function".
        
       | koakuma-chan wrote:
       | I have one better: the try block pattern.
       | 
       | https://doc.rust-lang.org/beta/unstable-book/language-featur...
        
         | oniony wrote:
         | Now that is pretty cool.
        
         | Sytten wrote:
         | I want that stabilized so bad but it's not been really moving
         | forward.
        
           | mmastrac wrote:
           | I was not a fan when I first saw it but I'm becoming
           | desperate to have it the more Rust I write.
        
           | koakuma-chan wrote:
           | #![feature(try_blocks)]
           | 
           | You only live once.
        
             | dwattttt wrote:
             | I've tried it recently, from memory error inference wasn't
             | that great through it.
        
               | JoshTriplett wrote:
               | That's exactly what's currently being fixed before
               | stabilizing it.
        
           | mbrubeck wrote:
           | There's some active work recently on fixing blocking issues,
           | e.g.:
           | 
           | https://github.com/rust-lang/rust/pull/148725
           | 
           | https://github.com/rust-lang/rust/pull/149489
        
           | stouset wrote:
           | Out of curiosity why can't a block just do this natively?
        
             | lunar_mycroft wrote:
             | Because then you couldn't use ? to propagate errors if they
             | occurred inside any loops or branches within the function,
             | which would be a significant limitation.
        
             | masklinn wrote:
             | Because it would massively alter langage semantics? It
             | converts returns from the nearest function into returns
             | from the nearest (try) block.
        
         | tayo42 wrote:
         | Why does this need special syntax? Couldn't blocks do this if
         | the expression returns a result in the end?
        
           | koakuma-chan wrote:
           | One reason is that would be a breaking change.
        
           | bobbylarrybobby wrote:
           | Try blocks let you encapsulate the early-return behavior of
           | Try-returning operations so that they don't leak through to
           | the surrounding function. This lets you use the ? operator 1.
           | when the Try type doesn't match that of the function this is
           | taking place in 2. when you want to use ? to short circuit,
           | but don't want to return from the enclosing function. For
           | instance, in a function returning Result<T,E>, you could have
           | a try block where you do a bunch of operations with Option
           | and make use of the ? operator, or have ? produce an Err
           | without returning from the enclosing function. Without try
           | blocks, you pretty much need to define a one-off closure or
           | function so that you can isolate the use of ? within its
           | body.
        
           | mwcz wrote:
           | The best part of try blocks is the ability to use the ?
           | operator within them. Any block can return a result, but only
           | function blocks (and try blocks) can propagate an Err with
           | the ? operator.
        
           | jeroenhd wrote:
           | Not without being able to use the ? operator.
           | 
           | The closest thing I can think of that will let you return a
           | result from within a separate scope using a set of foo()?
           | calls would be a lambda function that's called immediately,
           | but that has its own problems when it comes to moving and it
           | probably doesn't compile to very fast code either. Something
           | like https://play.rust-
           | lang.org/?version=stable&mode=debug&editio...
        
         | valcron1000 wrote:
         | One of the first things I tried in Rust a couple of years ago
         | coming from Haskell. Unfortunately it's still not stabilized :(
        
         | loeg wrote:
         | Can this just be done as a lambda that is immediately
         | evaluated? It's just much more verbose.                   let x
         | = (|| -> Result<i32, std::num::ParseIntError> {
         | Ok("1".parse::<i32>()?               + "2".parse::<i32>()?
         | + "3".parse::<i32>()?)         })();
        
           | saghm wrote:
           | My instinct is this would get hairy much faster if you want
           | to actually close over variables compared to using a block.
        
             | loeg wrote:
             | If the verbose return type syntax can't be elided, I think
             | it's more or less dead as a pattern.
        
             | ahartmetz wrote:
             | Not sure if that is relevant to your point, but: For better
             | and for worse, closing over any outer scope variables is
             | syntactically free in Rust lambdas. You just access them.
        
               | nicoburns wrote:
               | It's syntactically free, but it can cause borrow-checker
               | errors thst cause your code to outright fail to compile.
        
               | saghm wrote:
               | Yes, exactly. My concerns were semantic, not syntactic.
        
           | rendaw wrote:
           | That prevents other control flow mechanisms (return, break)
           | from operating past the function boundary. In general, I
           | avoid single-callsite functions as much as possible
           | (including the iterator api) for this reason.
        
             | ahartmetz wrote:
             | It sounds like you're fighting the language - Rust is sort
             | of FP-light and you're encouraged to return a null/error
             | value from the intermediate calculation instead of doing an
             | early return from the outer scope. It's a nice and easy to
             | follow way to structure the code IME. Yes, it's more
             | verbose when an early return would have been just right -
             | so be it.
        
               | dzaima wrote:
               | 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.
        
               | skribanto wrote:
               | Why couldnt you flatten it?
        
           | schneems wrote:
           | 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.
        
             | pflanze wrote:
             | 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...
        
         | satvikpendem wrote:
         | Ah yes, do-notation.
        
       | bobbylarrybobby wrote:
       | You can also de-mut-ify a variable by simply shadowing it with an
       | immutable version of itself:
       | 
       | let mut data = foo(); data.mutate(); let data = data;
       | 
       | May be preferable for short snippets where adding braces, the
       | yielded expression, and indentation is more noise than it's
       | worth.
        
         | bryanlarsen wrote:
         | 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.
        
           | kibwen wrote:
           | 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.
        
       | lights0123 wrote:
       | GCC adds similar syntax as an extension to C:
       | https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
       | 
       | It's used all throughout the Linux kernel and useful for macros.
        
         | Rucadi wrote:
         | The best part of statement expressions is that a return there
         | returns from the function itself, not from the statement expr.
         | 
         | I use that with with macros to return akins to std::expected,
         | while maintaining the code in the happy-path like with
         | exceptions.
        
       | ibgeek wrote:
       | This seems like a great way to group semantically-related
       | statements, reduce variable leakage, and reduce the potential to
       | silently introduce additional dependencies on variables. Seems
       | lighter weight (especially from a cognitive load perspective)
       | than lambdas. Appropriate for when there is a single user of the
       | block -- avoids polluting the namespace with additional
       | functions. Can be easily turned into a separate function once
       | there are multiple users.
        
       | bryanlarsen wrote:
       | More significantly the new variables x and y in the block are
       | Drop'd at the end of the block rather than at the end of the
       | function. This can be significant if:
       | 
       | - Drop does something, like close a file or release a lock, or
       | 
       | - x and y don't have Send and/or Sync, and you have an await
       | point in the function or are doing multi-threaded stuff
       | 
       | This is why you should almost always use std::sync::Mutex rather
       | than tokio::sync::Mutex. std's Mutex isn't Sync/Send, so the
       | compiler will complain if you hold it across an await. Usually
       | you don't want mutex's held across an await.
        
         | bryanlarsen wrote:
         | oops: Of course the Mutex is Sync/Send, that's the whole point
         | of a Mutex. It's the std::sync::MutexGuard that's not.
        
         | defen wrote:
         | Can this also affect stack usage? Like if `x` gets dropped
         | before `y` is introduced, can `y` reuse `x`'s stack space
         | (let's assume they are same size/alignment). Or does the
         | compiler already do that if it can see that one is not used
         | after the other is introduced?
        
           | loeg wrote:
           | Conceivably, yes.
        
         | tstenner wrote:
         | I have been using this in a web application that acquires a
         | lock, retrieves and returns a few variables to the outer scope
         | an then immediately unlocks the mutex again
        
       | emtel wrote:
       | There are some situations with tricky lifetime issues that are
       | almost impossible to write without this pattern. Trying to break
       | code out into functions would force you to name all the types
       | (not even possible for closures) or use generics (which can lead
       | to difficulties specifying all required trait bounds), and
       | `drop()` on its own is of no use since it doesn't effect the
       | lexical lifetimes.
        
         | nemo1618 wrote:
         | Conversely, I use this "block pattern" a lot, and sometimes it
         | _causes_ lifetime issues:                   let foo:
         | &[SomeType] = {             let mut foo = vec![];
         | // ... initialize foo ...             &foo         };
         | 
         | This doesn't work: the memory is owned by the Vec, whose
         | lifetime is tied to the block, so the slice is invalid outside
         | of that block. To be fair, it's probably best to just make foo
         | a Vec, and turn it into a slice where needed.
        
           | saghm wrote:
           | Unless I'm misunderstanding, you'd have the same lifetime
           | issue if you tried to move the block into a function, though.
           | I think the parent comment's point is that it causes fewer
           | issues than abstracting to a separate function, not
           | necessarily compared to inlining everything.
        
           | adrianN wrote:
           | Avoiding that kind of use after free problem is exactly why
           | people choose Rust, isn't it?
        
           | janquo wrote:
           | There is some experimental work for that here I believe:
           | 
           | https://doc.rust-lang.org/beta/unstable-book/language-
           | featur...
           | 
           | AFAIU it essentially creates a variable in inner scope but
           | defers drop to the outer scope so that you can return the
           | reference
        
       | keybored wrote:
       | Obligatory use: it's a block I guess
       | 
       | Voluntary use: I know this one. It's a pattern now.
        
       | esafak wrote:
       | Block _expression_ https://doc.rust-
       | lang.org/reference/expressions/block-expr.h...
       | 
       | Also in Kotlin, Scala, and nim.
        
         | IshKebab wrote:
         | 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).
        
       | etyp wrote:
       | This is one of those natural consequences of "everything is an
       | expression" languages that I really like! I like more explicit
       | syntax like Zig's labelled blocks, but any of these are cool.
       | 
       | Try this out, you can actually (technically) assign a variable to
       | `continue` like:
       | 
       | let x = continue;
       | 
       | Funnily enough, one of the few things that are definitely always
       | a statement are `let` statements! Except, you also have `let`
       | expressions, which are technically different, so I guess that's
       | not really a difference at all.
        
         | tialaramex wrote:
         | I'm not sure why you picked continue here? All the diverging
         | control flow instructions have the same type, ! aka "Never". In
         | stable Rust you're not allowed to use its name but it's "just"
         | an empty type and you can easily make one of those yourself -
         | an enum with no variants.
        
       | ngruhn wrote:
       | Reminds of Brian Wills OOP rant video from 2016. He advocates
       | exactly for this pattern:
       | https://www.youtube.com/watch?v=QM1iUe6IofM&t=2235s
        
       | skipants wrote:
       | I often employ this pattern in Ruby using `.tap` or a `begin`
       | block.
       | 
       | It barely adds any functionality but it's useful for readability
       | because of the same reasons in the OP.
       | 
       | It helps because I've been bitten by code that did this:
       | setup_a = some_stuff       setup_b = some_more_stuff
       | i_think_this_is_setup = even_more_stuff       the_thing =
       | run_setup(setup_a, setup_b, i_think_this_is_setup)
       | 
       | That's all fine until later on, probably in some obscure loop,
       | `i_think_this_is_setup` is used without you noticing.
       | 
       | Instead doing something like this tells the reader that it will
       | be used again:                 i_think_this_is_setup =
       | even_more_stuff              the_thing = begin         setup_a =
       | some_stuff         setup_b = some_more_stuff
       | run_setup(setup_a, setup_b, i_think_this_is_setup)       end
       | 
       | I now don't mentally have to keep track of what `setup_a` or
       | `setup_b` are anymore and, since the writer made a conscious
       | effort not to put it in the block, you will take an extra look
       | for it in the outer scope.
        
         | gleenn wrote:
         | Clojure also has the threading macro -> and ->> which are great
         | at converting exactly the same type of code into a stream of
         | modifications instead of breaking out everything into
         | variables. Naming things can be very useful sometimes but
         | sometimes it is entirely gratuitous and distracting to have
         | 
         | let input = read_input(); let trimmed_input = input.trim(); let
         | trimmed_uppercase_input = trimmed_input.uppercase();
         | 
         | ...
         | 
         | The extra variable names are almost completely boilerplate and
         | make it also annoying to reorder things.
         | 
         | In Clojure you can do
         | 
         | (-> (read-input) string/trim string/upcase)
         | 
         | And I find that so much more readable and refactorable.
        
         | ramses0 wrote:
         | JavaScript chiming in...                   function abc() {
         | let a = 1            {              let b = 2            }
         | console.log(typeof a)                  console.log(typeof b)
         | }          abc()
         | 
         | Used to do this occasionally for exactly the same reasons-
         | don't leave dangling variables junking up your scope, and don't
         | make weirdo functions with parameter passing that you'll only
         | ever call once!
        
       | atq2119 wrote:
       | Not mentioned in the article but kinda neat: you can label such a
       | block and break out of it, too! The break takes an argument that
       | becomes the value of the block that is broken out of.
        
         | the__alchemist wrote:
         | I just learned this one, and am gradually starting to use it!
         | It applies for loops too. I saw it in ChatGPT code, and had to
         | stop and look it up. Rust is a big language, for worse and for
         | better.
        
           | kibwen wrote:
           | I wouldn't call Rust "a big language" because of labeled
           | break. This is a pretty standard language feature, you can do
           | the same in C (and therefore C++), Go, Javascript, Java,
           | C#...
        
             | atq2119 wrote:
             | Those languages don't treat blocks as expressions, so you
             | really can't do the _same_ thing there. Something very
             | similar, yes. But not the same.
        
               | kibwen wrote:
               | Those languages aren't expression-oriented, so you would
               | need to assign the result to a previously-initialized
               | variable in a higher scope. But that just makes this
               | pattern clunkier in those languages. This subthread is
               | about jumping to labels, which is a relatively obscure
               | yet widespread feature supported by many languages
               | (though C and Go allow forward jumps, and the rest only
               | allow backward jumps, since the latter ensures that
               | control flow does not become irreducible).
        
           | tialaramex wrote:
           | break 'label value;
           | 
           | ... is something to be used _very_ sparingly. I reckon I
           | write a new one about once a year.
           | 
           | Very often if you think harder you realise you didn't want
           | this, you should write say, a function (from which you can
           | return) or actually you didn't want to break early at all.
           | Not always, but often. If you write more "break 'label value"
           | than just break then you are almost certainly Doing It
           | Wrong(tm).
        
             | the__alchemist wrote:
             | Not having put it into practice yet, there is a pattern I
             | use regularly which I plan to replace with the labeled one:
             | I set a flag at the top of the loop I have an inner loop.
             | The inner loop can set this flag. Directly past the inner
             | loop, I check for the flag, then break. I am pretty sure
             | this is exactly what the labeled break is for.
        
       | Rucadi wrote:
       | This is also somewhat common in c++ with immediate-invoked
       | lambdas
        
         | carstimon wrote:
         | A blog post for it from a prominent c++er
         | https://herbsutter.com/2013/04/05/complex-initialization-for...
        
         | jeroenhd wrote:
         | The same pattern can also be useful in Rust for early returning
         | Result<_,_> errors (you cannot `let x = foo()?` inside of a
         | normal block like that).                   let config:
         | Result<i32, i32> = {             Ok(
         | "1234".parse::<i32>().map_err(|_| -1)?             )         };
         | 
         | would fail to compile, or worse: would return out of the entire
         | method if surrounding method would have return type
         | Result<_,i32>. On the other hand,                   let config:
         | Result<i32, i32> = (||{             Ok(
         | "1234".parse::<i32>().map_err(|_| -1)?             )
         | })();
         | 
         | runs just fine.
         | 
         | Hopefully try blocks will allow using ? inside of expression
         | blocks in the future, though.
        
         | knorker wrote:
         | Yeah but languages that make you resort to this then don't let
         | you simply return from the block.
         | 
         | And the workarounds often make the pattern be a net loss in
         | clarity.
        
       | HackerThemAll wrote:
       | This is a great addition to the best patterns and practices in
       | Rust. Worth noting and using. In JavaScript there's the proposal
       | of "do expressions" which accomplish the same.
        
       | andrepd wrote:
       | I use this all the time. It's features like these that sell Rust
       | for me honestly; even if you wrapped your whole program in
       | `unsafe` it would still be a massively better language than C++
       | or C.
        
         | hu3 wrote:
         | Many languages use this idiom. Some popular ones even. So while
         | it's good that Rust joined them, it's hardly a differentiator.
        
           | andrepd wrote:
           | It's a differentiator wrt C and C++, is what I said.
        
             | exDM69 wrote:
             | Yes, sadly this isn't a part of standard C or C++.
             | 
             | It is available as a language extension in Clang and GCC
             | and widely used (e.g. by the Linux kernel).
             | 
             | Unfortunately it is not supported by the third major
             | compiler out there so many projects can't or don't want to
             | use it.
        
         | 9029 wrote:
         | C++ lambdas can be used to achieve a similar result, not as
         | pretty though
         | https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines...
         | But in general I agree!
        
           | knorker wrote:
           | The fact that you can't return from there makes for a huge
           | difference, though,
        
       | yearolinuxdsktp wrote:
       | I think the technique is important to have in your vocabulary,
       | but I think the examples given are a weak sell.
       | 
       | In the example given, I would have preferred to extract to a
       | method---what if I want to load the config from somewhere else?
       | And perhaps the specific of strip comments itself could have been
       | extracted to a more-semantically-aptly named post-processing
       | method.
       | 
       | I see the argument that when extracted to a function, that you
       | don't need to go hunting for it. But if we look at the example
       | with the block, I still see a bunch of detail about how to load
       | the config, and then several lines using it. What's more
       | important in that context---the specifics of the loading of
       | config, or the specifics of how requests are formed using the
       | loaded config?
       | 
       | The fact that you need to explain what's happening with comments
       | is a smell. Properly named variables and methods would obviate
       | the need for the comments and would introduce semantic meaning
       | thru names.
       | 
       | I think blocks are useful when you are referencing a lot of local
       | variables and also have fairly localized meaning within the
       | method. For example, you can write a block to capture a bunch of
       | values for logging context---then you can call that block in
       | every log line to get a logging context based on current method
       | state. It totally beats extracting a logging context method that
       | consumes many variables and is unlikely to be reused outside of
       | the calling method, and yet you get delayed evaluation and single
       | point of definition for it.
       | 
       | So yes to the pattern, but needs a better example.
        
         | ordu wrote:
         | _> what if I want to load the config from somewhere else?_
         | 
         | There are DRY and WET principles. We can argue which one of
         | them is better, but to move something used exactly once to a
         | method just due to an anxiety you can need it again seems to me
         | a little bit too much. I move things into functions that are
         | called once, but iff it makes my code clearer. It can happen
         | when code is already complicated and long.
         | 
         | The block allows you to localize the code, and refactoring it
         | into a separate function will be trivial. You need not to check
         | if all the variables are temporary, you just see the block,
         | copy/paste it, add a function header, and then add function
         | call at the place where the block was before. No thinking and
         | no research is needed. Veni, vidi, vici.
         | 
         |  _> The fact that you need to explain what's happening with
         | comments is a smell._
         | 
         | It is an example for the article taken out of a context. You'd
         | better comment it for the sake of your readers.
         | 
         |  _> I think blocks are useful when you are referencing a lot of
         | local variables and also have fairly localized meaning within
         | the method._
         | 
         | I do it each time I need a temporary variable. I hate variables
         | that exist but are not used, they make it harder to read the
         | code, you need to track temporaries through all the code to
         | confirm that they are temporaries. So even if I have just two
         | local variables (not "a lot of") and one of them is temporary,
         | I'd probably localize the temporary one even further into its
         | own block. What really matters is a code readability: if the
         | function has just three lines, it doesn't matter, but it
         | becomes really ugly if a lifetime of a variable overshoots its
         | usefulness for 20 lines of a dense code.
         | 
         | The other thing is mutability/immutability: you can drop
         | mutability when returning a value from a block. Mutability
         | makes reasoning harder, so dropping it when you don't need it
         | anymore is a noble deed. It can and will reduce the complexity
         | of reading the code. You'll thank yourself many times later,
         | when faced with necessity to reread your own code.
         | 
         | There is a code and there is the process of devising the code.
         | You cannot understand the former without reverse engineering
         | the latter. So, when you write code, the more of your
         | intentions are encoded somehow in your code, the easier it will
         | be to read your code. If you create temporary variables just to
         | parse config with the final goal to get the parsed config in a
         | variable, then you'd better encode it. You can add comments,
         | like "we need to parse config and for that we need three
         | temporary variables", or you can localize those three temporary
         | variables in a block.
        
       | ziml77 wrote:
       | Blocks being expressions is one of the features of the Rust
       | language I really love (and yes I know it's not something Rust
       | invented, but it's still not in many other popular languages).
       | 
       | That last example is probably my biggest use of it because I hate
       | having variables being unnecessarily mutable.
        
         | ghosty141 wrote:
         | In my opinion it's the 'correct' design, I don't see any
         | advantage from not doing this.
        
       | JDye wrote:
       | Our codebase is full of this pattern and I love it. Every time I
       | get clean up temporaries and expose an immutable variable outside
       | of the setup, makes me way too happy.
       | 
       | A lot of the time it looks like this:                 let config
       | = {           let config = get_config_bytes();           let mut
       | config = Config::from(config);
       | config.do_something_mut();           config.do_another_mut();
       | config       };
        
       | dionian wrote:
       | Scala has this too, it's extremely useful
        
       | saghm wrote:
       | For those who might not have seen it, you can use this to make a
       | `while` act like a `do-while` loop by putting the entire body in
       | the boolean clause (and then putting an empty block for the
       | actual body):                   // double the value of `x` until
       | it's at least 10         while { x = x * 2; x < 10 } {}
       | 
       | This isn't something that often will end up being more readable
       | compared to another way to express it (e.g. an unconditional
       | `loop` with a manual `break`, or refactoring the body into a
       | separate function to be called once before entering the loop),
       | but it's a fun trick to show people sometimes.
        
       | AdieuToLogic wrote:
       | From the article:                 Here's a little idiom that I
       | haven't really seen discussed        anywhere, that I think makes
       | Rust code much cleaner and        more robust.              I
       | don't know if there's an actual name for this idiom; I'm
       | calling it the "block pattern" for lack of a better word.
       | 
       | This idiom has been discussed and codified in various languages
       | for many years. For example, Scala has supported the same thusly:
       | val foo: Int = {         val one = 1         val two = 2
       | one + two       }
       | 
       | Java (the language) has also supported[0] similar semantics.
       | 
       | Good to see Rust supports this technique as well.
       | 
       | 0 -
       | https://docs.oracle.com/javase/tutorial/java/javaOO/initial....
        
       | domlebo70 wrote:
       | We do this via run in TS:                   export const run =
       | <T>(f: () => T): T => {           return f();         };
        
         | notpushkin wrote:
         | Can you clarify why do you prefer this over an IIFE `(() =>
         | {...})()`?
        
           | deredede wrote:
           | I like it. IIFEs always make me nervous because they look
           | like they beg to be removed if you don't know why they are
           | used. Using an explicit function such as `run` looks much
           | more intentional, and provide a single intuitive place (the
           | documentation of the `run` function) to explain the pattern.
        
       | icar wrote:
       | In Rust everything is an expression, yes.
        
         | steveklabnik wrote:
         | Almost everything.
        
       | chrismorgan wrote:
       | I feel like indentation is a really useful structural signal that
       | has been hijacked, in C-family languages, by unnecessarily strict
       | conventions and most recently by autoformatters, to correspond
       | _exclusively_ to language structure, when it could be used for
       | semantic structure as well (or occasionally instead).
       | 
       | Much of the value of this block pattern is that it makes the
       | scope of the intermediate variables clear, so that you have no
       | doubt that you don't need to keep them in mind outside that
       | scope.
       | 
       | But it's also about logical grouping of concepts. And _that_ you
       | can achieve with simple ad hoc indentation:                 fn
       | foo(cfg_file: &str) -> anyhow::Result<()> {           // Load the
       | configuration from the file.               // Cached regular
       | expression for stripping comments.               static
       | STRIP_COMMENTS: LazyLock<Regex> = LazyLock::new(|| {
       | RegexBuilder::new(r"//.*").multi_line(true).build().expect("regex
       | build failed")               });                    // Load the
       | raw bytes of the file.               let raw_data =
       | fs::read(cfg_file)?;                    // Convert to a string to
       | the regex can work on it.               let data_string =
       | String::from_utf8(&raw_data)?;                    // Strip out
       | all comments.               let stripped_data =
       | STRIP_COMMENTS.replace(&config_string, "");                    //
       | Parse as JSON.               let config =
       | serde_json::from_str(&stripped_data)?;                // Do some
       | work based on this data.
       | send_http_request(&config.url1)?;
       | send_http_request(&config.url2)?;
       | send_http_request(&config.url3)?;                Ok(())       }
       | 
       | (Aside: that code is _dreadful_. None of the inner-level comments
       | are useful, and should be deleted (one of them is even
       | misleading). .multi_line(true) does nothing here (it only changes
       | the meanings of ^ and $; see also .dot_matches_new_line(true)).
       | There is no binding config_string (it was named data_string).
       | String::from_utf8 doesn't take a reference. fs::read_to_string
       | should have been used instead of fs::read + String::from_utf8.
       | Regex::replace_all was presumably intended.)
       | 
       | It might seem odd if you're not used to it, but I've been finding
       | it useful for grouping, especially in languages that aren't
       | expression-oriented. Tooling may be able to make it foldable,
       | too.
       | 
       | I've been making a lightweight markup language for the last few
       | years, and its structure (meaning things like heading levels,
       | lists, _& c._) has over time become almost entirely indentation-
       | based. I find it _really_ nice. (AsciiDoc is violently flat.
       | reStructuredText is mostly indented but not with headings.
       | Markdown is mostly flat with painfully bad and footgunny rules
       | around indentation.)
       | 
       | --***--
       | 
       | A related issue. You frequently end up with multiple levels of
       | indentation where you really only want one. A simple case I wrote
       | yesterday in Svelte and was bothered by:
       | $effect(() => {           if (loaded) {               ... lots of
       | code ...           }       });
       | 
       | In some ancient code styles it might have been written like this
       | instead:                 $effect(() => { if (loaded) {
       | ... lots of code ...       } });
       | 
       | Not the prettiest due to the extra mandatory curlies, but it's
       | fine, and the structure reasonable. In Rust it's nicer:
       | effect(|| if loaded {           ... lots of code ...       });
       | 
       | But rustfmt would insist on returning it to this disappointment:
       | effect(|| {           if loaded {               // ... lots of
       | code ...           }       });
       | 
       | Perhaps the biggest reason around normalising indentation and
       | brace practice was bugs like the "goto fail" one. I think there's
       | a different path: make the curly braces mandatory (like Rust
       | does), and have tooling check that matching braces are at the
       | same level of indentation. Then the problem can't occur. Once
       | that's taken care of, I really see no reason _not_ to write
       | things more compactly, when you decide it is nicer, which I find
       | quite frequently compared with things like rustfmt.
       | 
       | I would like to see people experiment with indentation a bit
       | more.
       | 
       | --***--
       | 
       | One related concept from Microsoft: regions. Cleanest in C#,
       | `#region ...` / `#endregion` pragmas which can introduce code
       | folding or outlining or whatever in IDEs.
        
       | j16sdiz wrote:
       | > This is why I generally avoid C's "bottom-up" strategy for
       | organizing code.
       | 
       | I think the author misunderstood something....
        
         | charlie-83 wrote:
         | Yeah, language choice and the way your organise your code seem
         | orthogonal to me
        
       | sonu27 wrote:
       | When would you use the block pattern vs creating a new function?
        
       | rienbdj wrote:
       | This is Rusts OCaml roots showing :)
        
       | pkulak wrote:
       | 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.
        
       | Mawr wrote:
       | 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.
        
       ___________________________________________________________________
       (page generated 2025-12-20 23:02 UTC)