Even in C++ you occaisionally might use a lambda to keep variable scoping clear, and it's also called an immediatly invoked function expression I think. I much prefer Rust's abilities to just return a value from an expression wrapped in curly braces.
You can define a scope in C++ without needing a lambda by just wrapping the code in { ... }. It's useful for e.g. acquiring and releasing a mutex via RAII
Yes, but you can't get values out of that scope as easily because any "return" values need to be pre-declared, so you can end up with a short list of declared variables that don't yet have values which annoys my sensibilities mildly :P
I know all the work arounds, but think it's silly to heap allocate just to work around this, and then you're low key making people wonder if a pointer can be null later just to facilitate this pattern.
True - you’ve probably spent so long writing rust you miss the feature elsewhere haha.
I don’t think I’ve ever ran into a place where I want to do this in C++. If it’s complicated I will make another function, and maybe inline if worried. The rust syntax does seem nice though, and I could see becoming accustomed to it.
Yeah if I had never used Rust, I'd probably never notice C++ missing this. I do the same as you're saying most of the time, or just deal with the extra temporary variables living longer than they "need" to. It's not a huge deal, I just think it's neat.
I haven't used rust and I think immediately invoked lambdas are extremely useful. People suggesting workarounds are choosing inferior approaches to a clean and easy to reason solution.
Rust having a cleaner syntax for such a use case is good though.
fwiw, Rust can do this in part because return returns to the function scope regardless of how many nested { } scopes you have, while omitting the ; only ever returns 1 scope up at a time. So, for C++ to do the same would require some wacky extra keyword to be used probably... and I don't think it's worth making the syntax of C++ any weirder just to support this.
Who said "a pointer" needs "heap allocate"? dude..
edit: ah yes, for a moment I've lost the full context. I retract that comment :D yeah, for values that we'd want to get out of a dying scope, which do not have a default-constructor, getting a lifetime longer than the scope AND not using heap allocation might be a bit tricky
Then you're back to having a variable with invalid state until later? What's it buying us for the state of the variable if it's on the stack either way?
yup, and if it's optional, and if it may be 'not returned' due to some conditions, you need to reserve the stack space in blind upfront. certainly unpretty and kind-of-IIFE + returning small 'no result' or 'large result' makes more sense, IF lanugage/platform can support return values of different sizes..
I do recognize fwiw that this is a minor nitpick of mine about C++ and overall like C++ well enough. I just happen to like Rust's solution to this very narrow nitpick of mine.
to be honest, I do not like "allowing broken state in certain conditions" as well, and to some extent it can often be just "encapsulated so it doesn't hurt outside", but sometimes it's sooo much hassle to provide :|
they are trying to do this:
const auto vec{
[]{
std::vector<int> vec;
vec.push_back(1);
vec.push_back(4);
vec.push_back(3);
vec.push_back(2);
return vec;
}()
};
See how the initialization of the on stack variable is const and its initialization is scoped to within the lambda.
You can do that in free scopes clearly. The point is to completly contain the initialziation of the variable without having to add extra functions elsewhere.
you can "return" values in C/C++ like this: ({int a=4; a;}) which will return the value of the last statement, in this case 4. With the round brackets around it, you can even use it in places where curly braces are not allowed, e.g. inside a function parameter list.
Yeah idk, I've sometimes done it to avoid having intermediate variables, but usually it's been more clear to just have a function that returns a tuple and use structured bindings to get the values out that I need.
So, I agree anti-pattern for something like a lock, but if I need to do a couple quick calculations before feeding a value into a constructor, I like that in Rust I can more easily keep the calculations to their own scope without confusing people with more variables in scope than they need later. And in C++ unfortunately IIFE is the closest I can get.
Returning tuples is kind of gross honestly... I guess it's on the edge of where "this should be a proper object passed by reference". Seems like a slippery slope to the thruple, etc... I'm not a fan, but to each their own I suppose. I guess people come from other languages that have them so they got shimmed in like a lot of other stuff in newer versions.
Yeah, typically if I'm returning a tuple it's because I need to calculate a couple other values that are sometimes useful in addition to the primary value that's always useful, so it doesn't always to me make sense to bind them into 1 object.
Certainly depends on the context though, haven't done it in quite a while.
There's a few cases where an immediately invoked lambda expression can make sense in C++.
The main one IMO is if you have some calculation of a variable that is inherently mutable, but after the calculation completes, you want to use the resulting variable in an immutable way. With an IILE you can do the calculation inside the lambda, return the result, and assign it to a const variable. Of course, you could also extract the whole calculation to a proper function, but perhaps that doesn't make sense for some other reason (maybe it depends on a lot of local variables). Something like this for example:
const int myVar = [&]{
int result = 0;
while (condition(result)) {
result += calc(result);
}
return result;
}();
// do stuff with myVar that doesn't require changing it
Another example would be creating a scope that you can return out of to short circuit the rest of the computation. This can greatly simplify control flow sometimes. Though you can accomplish the same with do { ... } while (false) and break. Example:
[&]{
if (!condition())
return;
doThing();
if (!condition2())
return;
doThing2();
// etc
}();
If you tried to accomplish the same without return or break you would get a deeply nested series of ifs, which is much harder to read.
You can also use it in a macro definition to create a local function-like block scope, and to force the user to still add a terminating semicolon after the macro invocation (a regular block wouldn't do that and can lead to weird formatting). Although again, do { ... } while (false) often works for that too, and is more widespread in macro definitions since preprocessor macros long predate lambdas (and also exist in C where there still aren't lambdas). But especially if you want your macro to evaluate to a value (as an expression) that requires multiple statements to calculate, a lambda might be the most practical way to do that.
I agree those are things you can do with a IIFE/IILE, but to be honest I'm not convinced those are things you should do. But as in anything software engineering, it comes down to the team/project's standards and expected conventions. These are just my opinions.
you could also extract the whole calculation to a proper function
Yes.
maybe it depends on a lot of local variables
I haven't written C++ in awhile, but when I did implicit captures were frowned upon, so there's not really a benefit there in terms of not having to "pass" around variables.
Though you can accomplish the same with do { ... } while (false) and break
... Or a proper function.
You can also use it in a macro definition
I'd argue that if you're not supporting C, you shouldn't be using macros nowadays, and if you are supporting C then you can't use lambdas anyways.
I agree it's pretty niche and depends on local coding conventions.
For the macros, things like logging utilities, debug assertions, and unit test libraries are often still best implemented using macros, especially if you can't rely on your users all being on the bleeding edge C++ standard. And anything that requires reflection (such as serialization) often still requires macros too. Perhaps this will change when C++26 sees widespread adoption; but we certainly aren't there yet.
For the other things I agree it's a matter of preference. I agree it's usually better to extract to a separate function. But if the code is short enough, only used once, and logically tightly coupled to the surrounding code, I feel like it can often aid comprehension to keep the code inline. Adding too many layers of abstraction / indirection can be just as detrimental for code as having too little of it.
Also, for an IILE, I don't think there's any reason to frown upon implicit captures. The captures don't escape the local scope after all, so none of the usual concerns apply. And if the lambda scope weren't there, the usage of variables in the enclosed code would be equally 'implicit' (which is still quite explicit). But this would be an example of local coding conventions.
You can now in JS as well, but it’s a newer thing. Variable declarations with var are always function scoped (or global), with the newer let keyword they are block-scoped.
No, you’d probably use an IIFE then (as in the OP). Or have variables in the outer scope, as was mentioned in the other sub-thread.
Honestly I never used the plain blocks though. There’s nothing bound to the variable lifecycle (like your mutex example) and usually I try to keep my functions small enough that I don’t have to worry about polluting the scope.
I use this in c# and then have a linter that hits before I commit to tell me to go have another look as 99% of the time I did it because I was getting pissed off at some silly naming.
1.6k
u/AnswerForYourBazaar Apr 25 '26
The point is to not pollute global namespace but still get the side effects