I have one better: the try block pattern. https://doc.rust-lang.org/beta/unstable-book/language-featur...
let x = (|| -> Result {
Ok("1".parse::()?
+ "2".parse::()?
+ "3".parse::()?)
})();51–60 of 121 posts
I have one better: the try block pattern. https://doc.rust-lang.org/beta/unstable-book/language-featur...
let x = (|| -> Result {
Ok("1".parse::()?
+ "2".parse::()?
+ "3".parse::()?)
})();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?
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.
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™.
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
};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:: ()?) })();
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.
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.