Live data from Hacker News

Rust's Block Pattern

notgull.net

51–60 of 121 posts

Re: Rust's Block Pattern

#51

I have one better: the try block pattern. https://doc.rust-lang.org/beta/unstable-book/language-featur...

Can this just be done as a lambda that is immediately evaluated? It's just much more verbose.

    let x = (|| -> Result {
         Ok("1".parse::()?
          + "2".parse::()?
          + "3".parse::()?)
    })();

Re: Rust's Block Pattern

#52
post #25

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::sy…

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?

Conceivably, yes.

Re: Rust's Block Pattern

#53
post #49

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.

In my opinion it's the 'correct' design, I don't see any advantage from not doing this.

Re: Rust's Block Pattern

#54
post #23

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.

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.

  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™.

Re: Rust's Block Pattern

#56
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
  };

Re: Rust's Block Pattern

#58
post #51

I have one better: the try block pattern. https://doc.rust-lang.org/beta/unstable-book/language-featur...

Can this just be done as a lambda that is immediately evaluated? It's just much more verbose. let x = (|| -> Result { Ok("1".parse:: ()? + "2".parse:: ()? + "3".parse:: ()?) })();

My instinct is this would get hairy much faster if you want to actually close over variables compared to using a block.

Re: Rust's Block Pattern

#59
post #12

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.

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.
Post reply on HN