std::vector<int> suffix{3, 4};
auto append34 = make_appender(suffix); // Version 1: test will work
auto append34 = make_appender({3, 4}); // Version 2: test will fail
I was in a mood to type the examples but I accidentally made Version 1 because I had started with the author's first example. I wondered where the problem was and couldn't find any (neither running nor reading the code) until I noticed the author had changed to Version 2.
The problem is "obvious" in hindsight but one of the problems with C++ is that it makes certain things too implicit/convenient. I get that references are a staple feature in C++ but it's not at all obvious from the calling location that Version 2 is a bug. To see that, you have to navigate to the callee (finding which might be hard enough alone without a very solid IDE), and make sure that it's not capturing the argument by reference or not doing anything unsafe there.
It's often a problem when a seemingly "value" argument is turned into actually a pointer-to argument at the callee. There are other languages that have this too, and I've never liked it.
As someone who doesn't regularly code in C++ but has a solid understanding of the basics, I wonder why C++ ever allowed to have a reference parameter be called with a temporary? To me it feels like "References have value syntax but pointer semantics BUT you should program like it had value semantics"? Which to me would be exactly a premature optimization that is looking for trouble.
Again, this isn't a right/wrong thing. Rust moves by default (and a lot of people find "=" a weird pun for that), C++ copies by default and has rvalue-references and an explicit `std::move`.
If you want copy in your C++ lambda, you start it `[=](...) {...}`, if you want take a pointer in your C++ lambda, you start it `[&](...) {...}`, if you want something trickier you do trickier stuff.
Rust opts you in to the nitpicky static analyzer and you have to opt out with `unsafe`, in C++ you have to opt in with e.g. `clang-tidy` or some annotations.
They are remarkably similar, just with different defaults.
> They are remarkably similar, just with different defaults.
I'm going to lead with this, because I think it's most important: Culturally there's a world of difference. Safety is a part of Rust's culture. "Culture eats strategy for breakfast".
Take sorting. In C++ the default sort is unstable, while in Rust the default sort is stable, that's just those defaults you mentioned (each has both kinds), although the choice speaks to culture. But look closer, in C++ the sort has undefined behaviour if your type isn't totally ordered. In Rust you can't sort the partially ordered types without saying how to order them fully. Still, in both languages we can write a custom order, so what happens then? In C++ if your custom order is nonsense you get... undefined behaviour. In Rust sorting won't necessarily work with a nonsense custom order but the behaviour remains well defined.
> Rust opts you in to the nitpicky static analyzer and you have to opt out with `unsafe`
Unsafe gives you a small number of dangerous "super powers" needed to write efficient low-level code, it does not opt out of the borrow checker's analysis, or indeed most other checks. This misconception makes me wonder how much of what you've written is conjecture rather than practical experience.
The sibling says that C++ has the wrong defaults, full stop.
Well in Rust the default `HashMap` uses a cryptographic hash, and you see it everywhere, it's the de facto community "default". In C++ the community "default" is `absl::flat_hash_map`/`folly::F14`, which use SIMD to compare a whole stripe of key-prefixes simultaneously.
I want different defaults for different programs, but the idea that it's esoteric to ever want an associative container within arms reach that fucking demolishes the other one is, ugh, God I want to like Rust even more than I do but this "we're right and everyone else hasn't seen the light" routine is infuriating and pushes me at least away.
My parent comment is trying to emphasize that this isn't a right/wrong thing, different tools for different jobs. And people are just like: "nope, everything but Rust is wrong".
I like and use both C++ and Rust. I also have plenty of bones to pick with both languages.
However, I’ve never gotten the sense that Rust itself promotes the idea that “everything except for Rust is wrong.” I also don’t read much on the Internet these days, and I’m not doing so, probably avoid much of the hype that people are pushing about Rust.
Since it has been established as Hot New Thing, there are huge social incentives tied up in promoting it.
Why do you think that a language with better details would prevent you from opting in to whatever non-default behavior you need?
Having the "right" defaults is better for everyone. Folks who don't know or care get a good, safe default with no undefined behavior or unexpected danger, and folks who know better can opt into something that fits their needs explicitly.
Hashbrown (the Swiss Tables implementation) replaced the previous HashMap implementation in July 2019.
The port is a little older, 2018. The idea was famously explained at CppCon 2017, I don't know whether Google had published on Swiss Tables before that year.
The (default) hash for Rust's HashMap and HashSet is a SipHash. People shouldn't call this a "cryptographic hash" or a "crypto hash" - that's misleading as it would lead you to think of algorithms like the SHA-2 family - but this is literally a cryptographic algorithm just one with very specific properties suitable for this task.
Such algorithms are crucial to avoid being subject to a Denial of Service attack which is, in fact, a security problem. Of course under the C++ "blame the programmer" philosophy you don't deserve protection from Denial of Service unless you knew you needed that and figured out how to ask for it properly.
Just as with the sort functions this is about safe defaults, not about constraining people who know what they're doing. Dropping in FNV instead of SipHash, or even using the identity function as a "hash", is not difficult if you are sure that's what you need.
I clearly spoke out of turn when I mouthed off about Rust's table not defaulting to a Swiss design, and I thank you for straightening me out.
But to the degree that C++ has an RTFM vibe, and I really don't think you'll hear Andrei or Meyers or Sutter talking that way much, it's uniformly applied and not particularly partisan. In my experience C++ pros would rather be writing Haskell and that's where you get all these over-templated "header only" libraries.
Rust is in a glass house on "blame the programmer" stuff, because it's "blame the programmer for being dumb enough to not be using Rust exclusively".
The memory safety catechism makes sense for my SSH client, or web browser, or web server, or shell, or another few dozen security critical things. And frankly I'd feel safer if someone did a ground-up reimplementation of `bash` in Rust, I'd use it in a heartbeat. Tailscale writes their shit in Go for a reason, you don't want even the possibility of a use-after-free in your VPN.
But most of the software I run? It runs as me, and if someone is running as me behind the firewall, I'm in deep shit already.
`rustup` has `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` on the fucking home page, ditto vim/emacs extensions, etc. I'm calling bullshit on the every damned thing needs to be DoS or timing-attack hardened. It's marketing, and my original point is that all the other great modern features of Rust make a much better list of talking points.
> I'm calling bullshit on the every damned thing needs to be DoS or timing-attack hardened
But that isn't the claim. Rust's defaults are safe. Remember Rust's one line description "A language empowering everyone to build reliable and efficient software".
This is like with the decision that Rust's sort() is a stable sort. I know what a stable sort is, and so do you, so if we care we may decide it's appropriate to use the unstable sort which could be faster. But programmers who don't know what a stable sort is aren't expected to learn about it before their sort does what they expected.
Same here, I know that SipHash is slower than Fowler–Noll–Vo, which in turn is slower than the identity function, and I know why it would or would not be OK to choose them, and presumably you do too. So if we care we may choose a different hasher for our HashMap. But programmers who don't know about hash algorithms aren't expected to go learn all this stuff before using HashMap.
I think maybe C++ isn't programming it's actually a live action "Um, actually" game where the stakes are your program arbitrarily misbehaves unless you correctly guessed all the things wrong with whatever code you just wrote despite the compiler insisting there's nothing wrong with it as written.
Could I do OK at that game? I'd like to think so. Do I want to play? No thanks.
const doesn't make a difference in this case. It's about the passed-by-reference object being destroyed after the function returns. That is because the object was passed as a temporary.
The problem is "obvious" in hindsight but one of the problems with C++ is that it makes certain things too implicit/convenient. I get that references are a staple feature in C++ but it's not at all obvious from the calling location that Version 2 is a bug. To see that, you have to navigate to the callee (finding which might be hard enough alone without a very solid IDE), and make sure that it's not capturing the argument by reference or not doing anything unsafe there.
It's often a problem when a seemingly "value" argument is turned into actually a pointer-to argument at the callee. There are other languages that have this too, and I've never liked it.
As someone who doesn't regularly code in C++ but has a solid understanding of the basics, I wonder why C++ ever allowed to have a reference parameter be called with a temporary? To me it feels like "References have value syntax but pointer semantics BUT you should program like it had value semantics"? Which to me would be exactly a premature optimization that is looking for trouble.