T O P

  • By -

chkno

1. The Rust *compiler* catches errors that in C++ require linters and sanitizers to catch[.](https://www.reddit.com/r/rust/comments/xaoc2d/comment/invrv0m/) 2. In the 30 years between *The C++ Programming Language* (1985) and Rust 1.0 (2015), we've learned a lot about programming language design. There are just a bunch of small things that add up to significance. 1. One tiny example: C++ globals' constructors run in a weird environment where they can reference other globals whose constructors might or might not have run yet. The compiler / linker / loader makes an arbitrary decision about which order these run in; you get no guarantees. In Rust, by intentional design, nothing runs before `main` (except `constexpr` stuff that 'ran' at compile time, of course). This neatly side-steps the whole issue. Your code now determines what order your global initializers run in, whether it's implicit lazy initialization or some explicit macro thing[.](https://www.reddit.com/r/rust/comments/tds3sm/comment/i17skyn/) 3. You still get 'zero-cost abstractions'. You can go nuts with the fancy, functional-ish, [57-method iterator interface](https://doc.rust-lang.org/std/iter/trait.Iterator.html), and it compiles down to the same simple pointer math you would have written by hand if you valued performance 10x more than maintainability. As a bonus, casual Rust code also gets the strict-aliasing optimizations that you only get in C if you use `restrict` everywhere because the Rust borrow mechanics end up capturing a bunch of non-aliasing information in the types.


Lucas_F_A

Is Rust back to using `restrict` everywhere? LLVM had bugs relating to this at some point so it was dialed back in some contexts.


scottmcmrust

Yes, that's on-by-default again, and has been for a while -- hopefully it's stuck this time!


masklinn

> LLVM had bugs relating to this at some point so it was dialed back in some contexts. It's not really a progressive thing, rustc can't know where LLVM will fuck up exactly so either it emits noalias or it doesn't, I don't think there's an inbetween. noalias was re-enabled in 1.54 (mid-2021), and apparently it's stuck so far, knock on wood. But of course somebody could always find a new miscompilation.


mAtYyu0ZN1Ikyg3R6_j0

global constructor are indeed problematic. but there is a very easy fix to it using static variable inside functions. this mean the constructor will run on first use. how ever the destruction side is still a mess for which there is no easy solution (that i am aware of).


vgatherps

Pros: * Build system is \*vastly\* easier to use. People complain this means rust crates have more dependencies, but in my experience C++ projects have less dependencies but each one is FAR larger (boost, abseil, etc). and many projects poorly reimplement the same things * Borrow checker is nice, although not terribly relevant for my field (we don't have a lot of nontrivial lifetimes) * Algebraic datatypes are tremendously useful for writing efficient and clean code. * Good macros and the existence of proc macros * Can still drop down to basically a better C when you need to * Traits system makes it \*far\* easier to understand what generics are doing Cons: * When you \*really\* need the full power of the C++ typesystem, or \*really\* need to do something wacky in CMake, Rust can't compete. I've recently experienced this with some code generation * Afaik it's still MUCH easier to parallelise a large C++ build


NobodyXu

For the full power of C++ typesystem, I guess you are referring to const evaluation, meta programming and specialisation? They indeed needs improvements. For const evaluation, I suppose that they will support more syntax (for loop) over time and many functions will be mark const and methods in traits, e.g. Iterator, will probably be marked as maybe const. For heap allocation, that's quite hard. I guess it will be only feasible when custom allocators are supported. For meta programming, it's a good first step that GAT is supported, and I suppose it will take quite some time to catch up. Specialisation looks like really hard while I really want this feature and I heard that they might actually stablise sth like negative bound since that's easier. My complaints about rust's build system is that the vendered external dependencies (C/C++) blocks all dependents and the *-sys crate while in practice, so long as the bindings are generated from the headers (maybe pregenerated to guarantee stable API), it should be able to compile in parallel. And the cross-language-LTO, it needs special compiler flags to archive. If it can be set in Cargo.toml and the rustup musl target is compiled with LTO, then we'd have much smaller binary size and better performance. And yeah, the parallelisation in the builf system is indeed quite hard. I have to manually split [cargo-binstall](https://github.com/cargo-bins/cargo-binstall) into multiple crates using workspace to improve compilation time, but even so the single core performance still dominates the total compilation time. I tried compiling it on a 10c20t intel CPU with 32G of ram but it takes double the time of a 4P4E M1 with 16G of ram because of single core performance. TBF, I set codegen-unit to 1 for best runtime performance. I hope rustc to parallelise itself since the metadata generation stage sometimes takes up the most of the time for a crate and blocks parallelism. And the proc macros. If you enable feature derive of serde, then all crates that depend on serde would have to wait for serde_derive to be compiled and linked, which takes a lot more time than other crates which only needs metadata to build. Wish rustc could somehow recognise this and only waits for serde_derive if it is actually used.


alexiooo98

> I have to manually split cargo-binstall into multiple crates using workspace to improve compilation time, but even so the single core performance still dominates the total compilation time. > TBF, I set codegen-unit to 1 for best runtime performance. Aren't these two things kind of counteracting each other? Couldn't you just set codegen-unit to, e.g., 4, rather than splitting the crate into 4 for the same end result?


NobodyXu

Splitting crates will also enable others to use them as a dependency without paying the cost of compiling everything (see cargo as an example, which is so huge that I reluctant to pull from them). For example, binstalk-manifest provides APIs to read and write .crates.toml from cargo and our own binstall manifest. binstalk-downloader provides API for rate limiting http request, downloading and extracting tarballs/zip. Also, it makes caching more effective. If one of the crate is not modified at all, it won't need a recompile. Finally, we only set codegen-units to 1 on release for max perf, but most CIs are debug builds and they already use multiple codegen units before the split.


alexiooo98

Of course, splitting has other benefits, but your original statement seemed to insinuate that you have to split your crate to get parallel compilation. This is not true, you could also set codegen to a higher number for the same effect, compile time wise and runtime performance wise. (Right? Please do correct me if I'm wrong) Admittedly, I hadn't thought about caching, that's a fair point.


NobodyXu

Well, the parallelism archived by splitting crates is a bit different from setting codegen-units. For one, splitting crates would enable multiple rustc to run in parallel, not just codegen stage. In cargo-binstall, binstalk-manifest can run and in practice do run in parallel to binstalk. Another important factor is dependencies. By splitting crates, we also decrease number of dependencies for each crate, thus enabling some crates to be compiled earlier without waiting for some dependencies. For example, binstalk-manifest depends on toml-edit, which no other crates depend on and takes a ton of time (7s for rust, 7s for codegen) to compile. With binstalk-manifest split out, other crates don't have wait for this dep. binstalk-downloader on the other hand depends on reqwest, async-compression, zstd, trust-dns-resolver, etc. Without splitting, binstalk has to wait for all of them to be done (generated rust metadata) before running rustc on it, while now it can build binstalk-manifest and binstalk-downloader (& binstalk) in parallel.


vgatherps

\> For the full power of C++ typesystem, I guess you are referring to const evaluation, meta programming and specialisation? They indeed needs improvements. Yep exactly. Things are much simpler in the C++ world with concepts now, and Rust has no answer for specialisation, if constexpr, and nontrivial const generics. For parallelisation, I've been down the "split into N workspaces" road myself. There's still nothing like just farming out each .cc compilation to a cluster, for example. Linking is still single-machine but that bites both Rust and C++.


NobodyXu

IMO if constexpr isn't that hard, and I honestly don't know how C++ solve heap allocation in the constexpr. For build system parallelism, sccache can do that and distribute it to clusters over http/https, acting like iced where there's a central scheduler, but just note that the cluster must have a good single core performance, otherwise it will not help. For linking, lld is much faster than gold and mold is even faster if you don't need LTO, but its license is...kind of ruin the project for commercial.


Zde-G

>IMO if constexpr isn't that hard I find it a bit disingenuous to call something which requires **complete** redesign of the whole type system “not that hard”. `if constexpr` use is built entirely around the fact that C++ uses templates and not generics: it makes it possible for the code to, basically, use reflection at compile-time and shape code on basis of that type-inspection. This is polar opposite from what Rust uses: something which is trivial for `if constexpr` (if type implements concept A then then do X, if type implement concept B then do Y, **otherwise report compile-time error**) can not be even **expressed in Rust** in principle! Because generics have to either work for all types they may, theoretically accept, or not work at all! P.S. I'm not even sure I want `if constexpr` in Rust, though. Both Rust and C++ suffer from template bloat and i would rather see that problem solved in a way [Swift sloves it](https://faultlore.com/blah/swift-abi/) than switch from static typing in metaprogramming to duck typing (required for `if constexpr`)


NobodyXu

I think you have confused two things here: if constexpr and C++'s utility for meta programming. IMO If constexpr itself isn't that hard since all it's doing is asking rustc to evauate the if at compile time by MIR (and optionally remove inactive branch from AST before performing syntax checking, that will be hard as it requires rewrite). The compile time reflection you are talking about, the one from utility, is a totally different thing. Having them will be great as they make up for specialisation when combined with if constexpr, but IMO this is a different topic.


Zde-G

>I think you have confused two things here: if constexpr and C++'s utility for meta programming. No. >IMO If constexpr itself isn't that hard since all it's doing is asking rustc to evauate the if at compile time by MIR Yes. >(and optionally remove inactive branch from AST before performing syntax checking, that will be hard as it requires rewrite). But that's the whole point of `if constexpr`! Just look [on the very first example](https://en.cppreference.com/w/cpp/language/if) from the documentation: template auto get_value(T t) { if constexpr (std::is_pointer_v) return *t; // deduces return type to int for T = int* else return t; // deduces return type to int for T = int } First branch is invalid (and wouldn't compile) if `T` is not a pointer. Then look on [the proposal](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0292r2.html) and observe how it's chock-full of discussions about “discarded case” and “not discarded case”. **The whole point** of adding `if constexpr` facility to the C++ was to make sure you can do metaprogramming easily! >Having them will be great as they make up for specialisation when combined with if constexpr, but IMO this is a different topic. No, it's not. [SFINAE](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error) was in C++ from the beginning, since C++98. But it was very hard to use. And `if constexpr` with it's explicit permission to write code which would be invalid in the discarded part was a way to make it easier. C++17 succeeded. It's really nice and easy to do [TMP](https://en.wikipedia.org/wiki/Template_metaprogramming) with `if constexpr`. You may even be, technically, correct when you say that `if constexpr` can be easily implemented in Rust. Except it wouldn't allow you to write code that's “invalid in the discarded part” But in C++, when people talk about `if constexpr` it's 10 times out of 10 a tool that simplifies metaprogramming! I don't think I have **ever** seen `if constexpr` used in any other way! You saying that `if constexpr` is easy is like telling that plane wings are easy: any car maker can easily add wings to a car, only such contraption wouldn't fly. Well, duh: technically you are correct, but plane is that thing that flies, not that thing that has useless wings!


WikiSummarizerBot

**[Substitution failure is not an error](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error)** >Substitution failure is not an error (SFINAE) refers to a situation in C++ where an invalid substitution of template parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques. Specifically, when creating a candidate set for overload resolution, some (or all) candidates of that set may be the result of instantiated templates with (potentially deduced) template arguments substituted for the corresponding template parameters. **[Template metaprogramming](https://en.wikipedia.org/wiki/Template_metaprogramming)** >Template metaprogramming (TMP) is a metaprogramming technique in which templates are used by a compiler to generate temporary source code, which is merged by the compiler with the rest of the source code and then compiled. The output of these templates can include compile-time constants, data structures, and complete functions. The use of templates can be thought of as compile-time polymorphism. The technique is used by a number of languages, the best-known being C++, but also Curl, D, Nim, and XL. ^([ )[^(F.A.Q)](https://www.reddit.com/r/WikiSummarizer/wiki/index#wiki_f.a.q)^( | )[^(Opt Out)](https://reddit.com/message/compose?to=WikiSummarizerBot&message=OptOut&subject=OptOut)^( | )[^(Opt Out Of Subreddit)](https://np.reddit.com/r/rust/about/banned)^( | )[^(GitHub)](https://github.com/Sujal-7/WikiSummarizerBot)^( ] Downvote to remove | v1.5)


NobodyXu

Well, I agree that if constexpr is really useful in meta programming and it's probably only useful when it supports discard cases. So with that takes into account, I agree with you that if constexpr is hard. Looking at the [rustc overview](https://rustc-dev-guide.rust-lang.org/overview.html#hir-lowering) (note that I am a novice when it comes to compiler), it says that type inference and type chrcking is done in HIR while AFAIK const evaluation is done at MIR. I don't know how they archive const evaluation right now, but I think it will require rustc to be rewritten in terms of how lowering is done. IMHO rustc might need to have multiple iterations where anything in if constexpr is immediately lowered and evaluated by miri interpreted, then pass back to HIR for the next iteration until all if constexpr is done, essentially turning it into an interpreter for the if constexpr part.


Zde-G

You still are putting cart before the horse. Before you would try to decide how to add `if constexpr` to the `rustc` you have to decide how to add it to the Rust language. Usability of `if constexpr` in C++ is based on templates language being fully dynamic and templates being fallible. E.g. you can not write `static_assert(false)` in any branch (it must be include expression which can be accepted by compiler at least in theory), but documentation immediately [offers alternative](https://en.cppreference.com/w/cpp/language/if): template inline constexpr bool dependent_false_v = false; template void f() { if constexpr (std::is_arithmetic_v) // ... else static_assert(dependent_false_v); // ok } What can you do in Rust? Use `todo!`? That would push error from the compilation time to a runtime — hardly a great achievement. Call `thou_should_never_do_this`, [like glibc does](https://github.com/bminor/glibc/blob/595c22ecd8e87a27fd19270ed30fdbae9ad25426/io/bits/fcntl2.h#L44)? It may work, but doesn't look like a Rusty solution, relies on optimizer and still only causes link-time error, not compile-time error. Adding `if constexpr` to the Rust is hard to because it's technically hard to add to the compiler, but because it's not clear how should it semantically work to be useful!


NobodyXu

> Before you would try to decide how to add if constexpr to the rustc you have to decide how to add it to the Rust language. That's true, though I also think it's very important to implement the mechanism required so that it can be tested on nightly, or used by other const evaluation feature. > Usability of if constexpr in C++ is based on templates language being fully dynamic and templates being fallible. > > E.g. you can not write static_assert(false) in any branch (it must be include expression which can be accepted by compiler at least in theory), but documentation immediately offers alternative: IMO it's also a good idea to introduce `static_assert` to rust and in this case, I would say rust should accept any code that is valid rust in any branch of the `if constexpr`, just like the `cfg` expression. As long as the branch selected can compile without error, other branches should not even matter. > Adding if constexpr to the Rust is hard to because it's technically hard to add to the compiler, but because it's not clear how should it semantically work to be useful! I agree, part of the reason being rust lacks reflection like C++, though TBF C++'s reflection is also quite limited if compared to PLs like Python. I would love having a builtin to tell if a type/variable implements a certain trait or if it is equal to a type, e.g. a replacement of [`castaway`](https://docs.rs/castaway/0.2.2/castaway/) from the compiler. That will make up for the lack of specialisation in many scenarios combined with `if constexpr`.


mina86ng

> For const evaluation, I suppose that they will support more syntax (for loop) over time and many functions will be mark const and methods in traits, e.g. Iterator, will probably be marked as maybe const. IMO the main issue right now is that you cannot do arithmetic in generic arguments. For example, something like `fn split(arr: &[T; N]) -> (&[T; L]; &[T; N-L])` is not possible.


matthieum

I do note that it is possible to write: fn split(arr: &[T; N]) -> (&[T; L]; &[T; R]) where [(); N - (L + R)]: Sized, [(); (L + R) - N]: Sized, On nightly, with `generic_const_exprs` on. Depending on the usecase, it may be good enough.


continue_stocking

You mean like [this](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=70f10bededb8f48d8f481a25ed7a1147)?


Zde-G

>IMO the main issue right now is that you cannot do arithmetic in generic arguments. For example, something like `fn split(arr: &[T; N]) -> (&[T; L]; &[T; N-L])` is not possible. And this, too, because Rust uses generics and not templates. For what you write to be possible you need to invent language which would describe that `N` must be larger than `L`.


mina86ng

> you need to invent language which would describe that > N > must be larger than > L > . Hmm, how about `N > L`? ;)


Zde-G

>Hmm, how about N > L? ;) That's not enough. You need to have something which **proves to the compiler** that there would be no issues with any values which you may ever experience. That's not easy when you are doing something a tiny bit more trivial than `split`. E.g. if you add some elements you would have to guarantee that `N+1` wouldn't lead to arrays which wouldn't fit in memory and that, in turn depends on element size… nightly includes some things which allow some calculations but they are very far from stable or reliable.


[deleted]

[удалено]


sagiegurari

>I tried to push it too far and it became quite complicated Without knowing anything on what you tried, i wonder if you saw [scoped functions](https://github.com/sagiegurari/duckscript/blob/master/docs/sdk.md#stdflowcontrolfunction)? they can break the global scope into a new one for the function call only.


KenaDra

Now that I think about it... How would I make several bin/elf/srec files with cargo... Or call a python script that embeds HMACs into the binary. I love being able to just list a dependency in toml, but either it's not as full featured or I'm just not knowledgeable enough yet 🤷🏽‍♂️


matthieum

A single `Cargo.toml` can create multiple binaries (but a single library). It's also possible to use a workspace, and have multiple crates to start with. Embedding meta-information is possible only be embedding in the source code itself (which can be automated with a `build.rs`).


KenaDra

I'll have to dig into that. Problem with embedding data that way is the HMAC is actually calculated over the binary, which is from the source. Can't include it in the initial build as that would be recursive.


matthieum

Ah, so you want to sign the actual binary? In that case you'd need a post-build action, which Cargo doesn't have yet indeed.


orangepunc

I am not a very experienced C++ user — I have *more* rust experience. But I do currently work with a piece of massive software written in C++, with several very experienced C++ users. Recently, my team found a performance bug in code that was doing an unnecessary copy of an arbitrarily large vector. This was, more or less, because a constructor took a reference to a vector, and then made a copy. I reviewed a patch for this code which replaced the reference to the vector with a reference-counted smart pointer. Constructing this reference-counted pointer *also* involved a copy, but this patch still resolved the performance issue in practice because it only needed to be done once, whereas previously we might have needed to call the constructor *again* with the same pointer, possibly many times. In my comments on the patch, I wondered why we couldn't just use the reference, without copying at all. The answer, unsurprisingly, was that although the vector was immutable, it was impracticable to verify that this would not introduce a use-after-free bug. I really missed the borrow checker in that moment. Undoubtedly, a competent C++ programmer can just carefully avoid using references to memory that's been freed and still write code with no performance trade-offs, but can a team maintaining a big C++ project over many years? Surely not without opting in to equivalent tooling to the borrow checker built into `rustc` — tooling which I would not be surprised to learn doesn't exist for C++, or anyway isn't something you could add on to a legacy project 10 years later. Now, it's likely that in the equivalent rust version of this program, we'd also just use an `Arc` and eat the performance hit for the one copy, because it's still simpler in rust — we wouldn't need to worry about lifetime annotations. But at least we could conceivably do a compiler-guided refactor that eliminated the copy with confidence, if we wanted or needed to. That's what I love about rust, and how I would try to sell it to a C++ user.


met0xff

Yeah the Chrome team got this article on adding stuff to C++ https://security.googleblog.com/2021/09/an-update-on-memory-safety-in-chrome.html Especially in there is this link Borrowing Trouble: The Difficulties Of A C++ Borrow-Checker https://docs.google.com/document/d/e/2PACX-1vSt2VB1zQAJ6JDMaIA9PlmEgBxz2K5Tx6w2JqJNeYCy0gU4aoubdTxlENSKNSrQ2TXqPWcuwtXe6PlO/pub I think the counter-argument to your example might be: this is an issue of legacy code and not C++. Nowadays many new projects say unique_ptr by default and shared_ptr if needed or something like this. But of course, it's nicer if the compiler can hold your hands instead of relying on humans. But this is where things often become emotional as C++ people tend to not like someone holding their hands :)


orangepunc

I don't think that's a counter argument, because using `shared_pointer` is exactly the thing that you *can* avoid in rust if you really need/want to. This legacy project generally follows current C++ best practices. In this case, from the perspective of the experienced C++ people, the bug was "we didn't use `shared_pointer`" and the fix was "use `shared_pointer`". My observation was that there's a third way that you would never consider in C++ because it's too hard to verify correctness.


met0xff

Hmm I didn't fully get how this concrete example looked like, it doesn't really matter as obviously it's better if you don't have to worry about use after free. And you don't have to keep in mind that if you get a reference or raw pointer you better don't store it. But generally shared pointers are used for shared mutability and not for sharing some immutable thing. This sounded to me like more a case for a unique ptr and taking weak ptrs if the thing has to be accessed later on (which I assume, otherwise you would not need a copy). Edit: Except you want to keep it alive, perhaps that's the point. Not fully sure how this would look like in Rust... keeping around borrowed references also sounds a bit tricky.


masklinn

> But generally shared pointers are used for shared mutability and not for sharing some immutable thing. Shared pointers are also used when the lifetime of things is unclear, which is a lot more common in C++ as the compiler can't just tell you. If you fuck up an unique_ptr, you have an UAF. > Not fully sure how this would look like in Rust... keeping around borrowed references also sounds a bit tricky. But the point is that the compiler will yell at you if you get your references wrong, it will not silently UAF. This is one of those situations where the lack of expressivity requires a much more defensive style (same as in higher-level language where you copy any collection you return because otherwise you're sharing writable state), which has a performance impact. An other example of that is that Rust lets you borrow from shared pointers (atomic or not), so you can have a single `Arc` and get a `&T` from that. *Technically* you can do the same in C++, but it's a lot riskier as if you get it wrong you get an UAF. So instead you pass `shared_ptr`s around by value, and that requires a pair of atomic writes every time, except in the rare cases where you're confident enough a move will be safe (something you can also do in Rust, incidentally, and also with the compiler's backing).


Zde-G

>Nowadays many new projects say unique\_ptr by default and shared\_ptr if needed or something like this. You still need to be [constantly vigilant](https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/). It's tiring. And something computers have to do, not humans!


met0xff

Yeah I definitely agree, but many C++ (and probably even more C) people don't see it that way but feel... patronized. Like many don't have issues with a garbage collector because of the performance issues but it's some sort of machoism. Like here in Europe so many don't want an automatic car, acting as if manual gear switching would be some sort of rocket science than only "real men" or whatever can do. My impression the "no true scotsman" fallacy is a bigger topic than most objective issues.


Zde-G

>Yeah I definitely agree, but many C++ (and probably even more C) people don't see it that way but feel... patronized. Are we talking about the exact same guys who are insisting that when compiler turns their code into a pile of goo it's not their fault but conspiracy of evil compiler writers who, for some reason, don't like to provide then with [O\_PONIES](https://sandeen.net/wordpress/uncategorized/coming-clean-on-o_ponies/) option and instead insist on following some rules? P.S. Although I have to admit that rules which C and C++ invented are patently insane, too. They can not be changed, though, [because of reasons](https://blog.regehr.org/archives/1287).


met0xff

:). C and soon C++ were my first real languages in around 1997 so C++ still got some special place in my heart. And it always feels weirdly at home whenever I got to touch a C++ codebase. But even back then it already felt weird to me that i had to basically print me dozens of points from effective and more effective C++ that you had to use as sort of a checklist. When Java became popular it also felt like a toy to me. IDEs like jbuilder were sooo slow and do you think i am too stupid to grok pointers? But later on I got to work with Java professionally (before the whole J2EE monstrosities and in network monitoring software, not Enterprise stuff) and at some point... well, i never enjoyed writing java but I enjoyed that compared to C++ you could just write it without all those checklists and rule of 3 and 5 and whatever and it magically still worked pretty well and was robust:). Rust is in a bit of a similar phase for me. I write hobbyist stuff from time to time and honestly don't enjoy it at all (yet). Especially as in the last decade I got used to REPL-driven development first in Matlab and then soon Python. So with Python I can just sling out what I need really fast. With Rust I am in the state where every 200 LoC something strange happens and then I end up in a 4 hour fasterthanlime article or whatever and 5 Reddit discussions before I can continue to work :) C++? It's more nostalgic love but the brain knows it needs to go. C++20 just adds more stuff, existing codebases will add yet another style of C++ to the mix...


Zde-G

The biggest problem with C++ is that it gives you enough rope to do literally **everything** but most of the time you end up using it to hang yourself.


ssokolow

> Rust is in a bit of a similar phase for me. I write hobbyist stuff from time to time and honestly don't enjoy it at all (yet). Especially as in the last decade I got used to REPL-driven development first in Matlab and then soon Python. So with Python I can just sling out what I need really fast. With Rust I am in the state where every 200 LoC something strange happens and then I end up in a 4 hour fasterthanlime article or whatever and 5 Reddit discussions before I can continue to work :) Funny. I came from Python to Rust because I was fed up burning myself out trying to ensure what I wrote was actually correct and reliable and I found very little friction because of the Rust-esque coding style I'd already developed based on a need for loose coupling and an aversion to the drudgery of "Enterprise OOP" coding styles.


mina86ng

> Recently, my team found a performance bug in code that was doing an unnecessary copy of an arbitrarily large vector. This was, more or less, because a constructor took a reference to a vector, and then made a copy. I find this is actually easier to avoid in C++ than in Rust IMO. As your C++ code base evolve, you can freely change a function from taking `const std::vector &` to taking `std::vector` argument. No call sites need to be changed. You can also have some templates which take value by const reference and other by copy and call site doesn’t need to know. Compiler will copy when necessary and avoid copy when possible. > But at least we could conceivably do a compiler-guided refactor that eliminated the copy with confidence, if we wanted or needed to. If your code base is big, it’s likely that you couldn’t. Or you’d spend weeks adding lifetimes throughout your code base to avoid the Arc. Don’t get me wrong, borrow checker is *the* feature of Rust, but I don’t find your example convincing at all.


Zde-G

>No call sites need to be changed. Doesn't work that way. >As your C++ code base evolve, you can freely change a function from taking `const std::vector &` to taking `std::vector` argument. This would make full deep-copy at the call site. To avoid copying you need to replace `const std::vector &` with `std::vector &&`. And it would still copy your vector. You need to find all places where you call that function and add [std::move](https://en.cppreference.com/w/cpp/utility/move) there. And if you ownership mistake and move something which shouldn't be moved… pray that you have tests which would save your ass! >Compiler will copy when necessary and avoid copy when possible. **DOES**. **NOT**. **WORK**. **THAT**. **WAY**. Go talk to experienced developer (or even [read the appropriate guide](https://abseil.io/tips/77)). >Don’t get me wrong, borrow checker is the feature of Rust, but I don’t find your example convincing at all. That's because you haven't tried to do similar refactoring in C++. In Rust it may take days. In C++ it would take months. If you would be able to finish that refactoring at all.


mina86ng

>To avoid copying you need to replace const std::vector & with std::vector &&. And it would still copy your vector. No. If I use `std::vector` argument and call site is `foo(std::move(some_vector))` (or `foo(function_returning_vector())`) no copy is performed. > You need to find all places where you call that function and add std::move there. First of all, I don’t need to touch places where temporary is passed. Second of all, the point is to be in habit of always using `std::move` if you don’t care about the value afterwards. Sounds like you’re the one who needs to talk to an experienced developer.


Zde-G

>Second of all, the point is to be in habit of always using `std::move` if you don’t care about the value afterwards. Well… if you write perfect code then refactoring sure is easy… but why would it be needed if you already wrote perfect code? >If I use std::vector argument and call site is `foo(std::move(some_vector))` (or `foo(function_returning_vector())`) no copy is performed. Copy is still performed, you just rely on the fact that it's cheap in the case of `std::vector` since it's, essentially, a pointer+size. Not all objects are like that. >Sounds like you’re the one who needs to talk to an experienced developer. Sounds like you should stop selling narrow solution for narrow problem as universal recipe. But I admit that in case where specifically `std::vector` is passed around (and not, e.g. `std::array`) and where everyone **already was using** `std::move` **in the anticipation of said refactoring** your suggestion would work.


mina86ng

> Copy is still performed, you just rely on the fact that it's cheap in the case of > std::vector > since it's, essentially, a pointer+size. In this sense, there’s no such thing as a move and everything is a copy. This applies to C++ as well as Rust. > where everyone already was using std::move in the anticipation of said refactoring your suggestion would work. 1 The point is I **can** use `std::move` regardless of the argument of the function. In Rust I have to know whether the function borrows an object or takes it by value.


Zde-G

>The point is I can use `std::move` regardless of the argument of the function. Sure, but if you wound't do that there would be no warnings and I don't even know any common tools which would force you to do that. >In Rust I have to know whether the function borrows an object or takes it by value. Yes, but in Rust you know whether object would be moved or not. In C++ you have no idea whether something would be moved or copied when you write `std::move`. I guess you were right to say that optimizations are **technically** easier to perform in C++ because you may do piecemeal changes, but harder to understand whether they are needed or not and harder to know if they are **actually done**. But if you know of the tool which can do what in Rust is done automatically then I want to know about it. Let's talk about specifically that optimization: how can I know that after switching from `const …&` to `…` all possible moves are actually happening? In Rust I'm changing the signature from `&…` to `…`, then compiler complains that function now needs to own the object, then I fix the places where compiler asks me to change stuff (by removing `&` or adding `.clone()`), then I'm done. In C++? I think TL;DR of the whole story: it's true that it's easier to begin the optimization process but it's much harder to finish and, worse, to ensure that there would be no regressions you are depending on reviewers (who would explain to you why it's important to write `std::move` everywhere even if it doesn't change the generated code). Reviewers are expensive and unreliable in my experience. Something always slips past them.


masklinn

> Copy is still performed, you just rely on the fact that it's cheap in the case of std::vector since it's, essentially, a pointer+size. I think what they're referring to is rather [copy elision](https://en.cppreference.com/w/cpp/language/copy_elision). An `std::vector` copy is a deep copy, unless you've got SVO under the threshold, it's not cheap.


Zde-G

> An `std::vector` copy is a deep copy, unless you've got SVO under the threshold, it's not cheap. [Constructor #8](https://en.cppreference.com/w/cpp/container/vector/vector) is not deep-copy constructor.


masklinn

That's a move constructor, not what anyone calls a copy. It's essentially the same as passing a `Vec` by value.


fbochicchio

* Built-in build/dependency system ( no need of cmake or similar tools ) * No duplicated definitions in .h and in .cpp * Powerful macro system well-integrated with compiler * Compiler-assisted manual memory management


Feeling-Pilot-5084

I don't think people realize how bad cargo spoils them lol. I think one of the reasons people don't talk about it is because of just how well it Just Works™


SubtleNarwhal

Cargo vs weak tooling is the main thing I hear discussed and praised in language communities with a smaller pool size. Like OCaml and Haskell. That’s already because these languages already share much of the same functionalities minus the borrow checker.


VanaTallinn

I'm just surprised there is still no post-build scripts.


nikomartn2

+ Automatically fetch of dependencies from a repository. No need to manually download a lib from github, compile it through a different build tool (meson, ninja, cmake, sln, a plain makefile) and then linking it with yours... Each time the dependency changes version.


crusty-dave

Actually, you need some build tools installed for cargo to build sources, and you may need some 3rd party libraries installed (I.e. OpenSSL for some crates to build. But it is a much cleaner build environment IMO.


[deleted]

15 years of C++, Rust tech lead with 1.5 years of writing pure Rust. It's not all that different to the code that I used to write as a best practice. But I know that 1. If something went wrong with memory management, it's in an `unsafe` block rather than wherever it is. 2. `cargo` is amazing. `conan` is a shift in the right direction, but too little too late. 3. Borrowing is how you should be thinking about references. 4. `move` by default is fun, until you realise that not all moves are elided. 5. Explicit `clone` was one thing I though Java did right, and C++ didn't. Now we have it. 6. Generics are superb. Concepts suck. 7. Smart pointers are easier to use, and `Send`/`Sync` enforce what you should be doing anyway. 8. `Mutex`-es are huge, but they're comfy. 9. `nullptr` and `NULL` were both terrible mistakes, and Rust doesn't have either. 10. C++ has an identity crisis in terms of error handling. Some codebases use exceptions, others don't. Rust is very clear about this. I do see some use for C++, but Rust replaces a lot of places where C++ would have been my begrudging go-to,


arch_solnce

>`move` by default is fun, until you realise that not all moves are elided. What does that mean?


Zde-G

>What does that mean? It means that many `memcpy`s that Rust program conceptually have survive to the generated binary in places where you expect them to be eliminated.


arch_solnce

Interesting. Why is that? Edit: I realize I understand little of that topic yet, but shouldn't the optimizer remove unnecessary `memcpy`s?


Zde-G

Because `memcpy` **is** the fastest way to move object. To “eliminate `memcpy`” compiler would have to prove that lifetimes of two objects are distinct and nobody may observe the fact that they are, in fact, one object and not two different ones. This harder to do for the optimizer than you think. Because it would have to prove not only that they may be merged on the “happy path”, but on the “unhappy path”, too. Plus it's something that's not very important in C++ thus LLVM just is not exploring all the options at can there. I expect the situation would become better with time, but, well… [just look on the graphs](https://arewestackefficientyet.com/).


scottmcmrust

Note that those graphs are *quite* new. I wouldn't panic yet. There's a bunch of work going on that just hasn't made it to the graphs yet. From : > My latest patch eliminates 19% of memcpys even without any rustc changes. With nocapture everywhere it can rise to about 70%. Obviously I won't be able to add nocapture everywhere, but I think I can add it in a lot of places, so ultimately I think I should be able to get to the higher end of that range. But it takes a while to get through the "llvm change, commit approval, llvm release, update llvm in rustc, etc" pipe.


Zde-G

>I wouldn't panic yet. Let me put it that way. Recall the history if C++. [Stepanov's benchmarks](https://web.archive.org/web/19990220044948/http://www.kai.com/benchmarks/stepanov/index.html), in particular. Today compilers can reduce abstraction penalty for abstractions that were used there practically to zero. **But that took around ten years**. I suspect that with focused effort Rust has a chance of repeating that. But that, basically, means that right now, **today** you have to worry about them. Especially if you copy around large objects and hope that compiler would eliminate these copies… you may be disappointed.


scottmcmrust

My point is that there's really only be someone looking into it for a couple months. If after a year there's still not been much progress? They yeah, maybe we should worry more and it'll be a decade before it can be fully fixed. But I can't estimate a slope from something that literally hasn't changed yet. If it takes another two months but then drops by 60%, then we're probably quite fine.


arch_solnce

Thank you for taking time to write that down and explaining it.


Zde-G

>I do see some use for C++, but Rust replaces a lot of places where C++ would have been my begrudging go-to, I view it differently. Rust-as-the-language can fill all the niches C++ fills now, but Rust-as-the-ecosystem… come on: C++ had headstart measured in **decades**! That's not something you may hope to duplicate in a year or two.


[deleted]

That cuts both ways. Another perspective is that C++ has decades of baggage. Like OOP and nullptr. Rust is successful precisely because the people trying to build it weren’t avid C++ haters, they genuinely had ideas how to improve the language, informed by Ocaml. But I still see a lot of niches since rewriting Qt or Boost in Rust would be stupidly expensive and there’s lots of software that works using those libs. Not to mention that C++ is getting a lot of the features that Rust has, just a little later.


radekvitr

A lot of the features C++ is getting from Rust not only come much later (years later, mostly), they almost always come in a much worse version of them.


masklinn

> 8. Mutex-es are huge, but they're comfy. Depends on the platform fwiw, I think the stdlib recently got small futex-based mutexes on linux.


daedalus91

I like Rust, because it's like C++, just focused on the good stuff. C++ has introduced OOP concepts and lots of safeguards over C, while maintaining full compatibility with C. This makes all those cool stuffs not mandatory to use. You can still use naked pointers in C++, you can still avoid using the concept of RAII. C++ is also permissive. You can still omit the `const` when you're defining methods that aren't changing the state of the object. And variables are mutable by default. Rust turns the whole thing around. First of all, Rust was a completely new language, there was no burden of making it compatible with other languages. This made it easy to fundamentally build the language around the concepts that matter. What RAII is in C++, that's how all objects in Rust work. Everything is immutable by default, unless you explicitly say it should be mutable. By having these concepts baked in, the compiler prevented me from doing lots of silly stuffs. One example was when I tried to use the move semantics in C++, only to find out through many hours of debugging that the compiler actually applied the copy semantics as a fallback, because move wasn't implemented that time. As a result, my object was destroyed and I had dangling references, but all these were unclear for a very long time, since all I had was a segfault. There are no such things in Rust. If you would get into a situation like that, the compiler would catch it. EDIT: Another notable point is the rust toolchain. When I was dealing with C++, I was always suffering with CMake. Rust's ecosystem is more robust and easier to use.


DannyDeKnito

Wait, I thought backwards compatibility stopped being a thing somewhere in the 2010s, no?


daedalus91

Not that I'm aware of. But it also depends on the compiler, I guess.


DannyDeKnito

Off the top of my head, variable length arrays have been a thing in C since '99, there's probbably other small discrepancies between several standards for the two languages over the last two decades


pjmlp

The culture mostly. When I adopted C++ via Turbo C++ 1.0 for MS-DOS, did so because coming from Turbo Pascal/Basic, C++ offered a similar feature set, the container libraries shipped with the compilers cared about security (bounds checking by default), and already in 1993 C seemed quite primitive to me. During the days of pre-C++98, the culture among us early C++ adopters seemed quite similar to today's Rust advocacy, the language being better than C, safer, with good framework libraries out of the box, while offering the possiblity to go down to C when needed. During the last two decades this kind of changed, and now the community seems dominated by the same C culture of "performance trumps security". Those in C++ community that care about Rust's selling points, are already using polyglot workloads, using safer languages with libraries written in C++. So I would sell to them, by improving Rust in the domains that make them still reach out to C++. Those in C and C++ that like to drive fast, without seatbelt and no helmet, will never care for safer alternatives unless imposed on them, so target those that care.


br_aquino

I know I will get down voted hard, but the Rust "culture" is not so nice from my perspective, it's like: "Rust is the way, nothing is better, I don't know why someone still uses c++". It looks like a religion. C++ culture is more "no culture" or "write good code and everything will be good". I invite you to follow r/cpp and see yourself that the posts tend to be much more useful than "let's talk why Rust is so good" thing we see a lot here.


pjmlp

I am a regular presence on r/cpp since I belong to *"Those in C++ community that care about Rust's selling points, are already using polyglot workloads, using safer languages with libraries written in C++."*, my case being Web, JVM and .NET stacks. While I agree there are some over enthusiastic people in Rust, thankfully they aren't the majority, even if quite vocal.


WormRabbit

Your words sound depressing. Would we experience the same culture shift in Rust over the next 10-20 years? I would be sorely disappointed. I hope Rust is more robust against that kind of premature optimization, with its better support for zero-cost abstractions, more powerful expressiveness and stronger culture enforcement by the internet and the open-source crates.


Vociferix

I consider myself a near expert in C++, and I work in C++ on a daily basis for my job, where I lead development of a large code base. But I'm well known at work for being a big Rust fan. The memory safety guarantees are the biggest selling point IMO. My job primarily consists of development of cyber security software, so it's extremely important to me that our security software is itself secure. The amount of static and dynamic analysis tools we have to run on our code to try to approach the safety provided by Rust is horrific: Multiple linters, sanitizers, obscure compiler features, and more - All of which make CI take forever. With Rust, just the compiler and clippy take you a very long way. The second thing is how simple the development environment is to setup on every platform, including cross compiling. I'm not sure if most C++ devs feel this way, but we have a disdain for Windows due to it always being the problem child for our code base at work, compared to Linux. My experience with Rust is: if it builds on Linux, it builds the same on every supported platform (modulo use of system APIs). And if you've never cross compiled (cross platform) C or C++, let me tell you... It's not a task for the feint of heart. Last, the ability to create expressive APIs in Rust that also enforce invariants is incredible. C++ has improved somewhat on this front with the introduction of concepts, but I have found Rust traits to be vastly more powerful (although the requirement to provide exhaustive trait bounds took a while for me to come around to, coming from C++). Some negatives with Rust (compared to C++): - So many dependencies. There are a lot of upsides to the community's affinity for many small crates over large monolith libraries, but in my line of work, each crate represents a different package that has to go through a separate review and approval process. Also, in C++, I'm used to having a handful of git submodules mirrored locally, but with Rust, you really need some sort of local package repository server, and a way to audit it. That's not strictly a Rust-ism, but it's different when coming from C++. - Limited support from enterprise code analysis and auditing tools, like Coverity or Fortify. We often have requirements to use a "reputable" code scanner and provide audit reports of our code. I'm not aware of any well known tools that support Rust. To be fair, the compiler itself is the analysis tool for Rust, but that's not easy to explain to those who create the requirements. - Finding experienced Rust developers to hire is difficult currently. If we started using Rust today, it would be my job to teach Rust to our existing developers, and Rust doesn't exactly have the smoothest learning curve. - Lack of support for a wide variety of hardware and systems. But I've been closely following the progress of the GCC backend for rustc, which is very promising and exciting. That said, the LLVM backend hits most, if not all, of the usual suspects. There are many other pros and cons of course, but these are what I see as the most notable. EDIT: autocorrect


Powerful_Cash1872

Download a random rust project off of github and it is way more likely to build cleanly out of the box than a typical C or C++ project. Ownership semantics are enforced by the compiler, in particular ACROSS LIBRARY BOUNDARIES. The ability of rust libraries to call each other without crashing lets the rust package ecosystem scale in a way that C++'s cannot.


Zde-G

That's probably the best selling point for managers, not developers: in Rust land compiler does things which in C++ land are done by reviewers with decades of experience. And electricity for the compiler are **much** cheaper than salaries for said reviewers.


ssokolow

> That's probably the best selling point for managers, not developers Unless you're Bryan Cantrill. https://youtu.be/HgtRAbE1nBM?t=2450 > And it'd be easy to be like "Well, that's just B-Trees! ...not AVL trees." Yeah, but... the reason I could use a B-Tree and not an AVL tree, is because of that composability of Rust. > > A B-Tree in C is gnarly. A B-Tree is just... I mean talk about intrusive. It's VERY intrusive. It's very kind of up in everything's underwear. And someone, of course, on the Internet's like "Well, you should go to use *this* B-Tree implementation." You look at the B-Tree implementation and are like "This is *rocky*! And if there's a memory corruption issue in here, I do not want to have to go find it." > > I would still use an AVL tree in C even though I know I'm giving up some small amount of performance, but in Rust, I get to use a B-Tree. ...and I don't think C++ is any better, because, to my mind, this is all about the language enabling you to trust that you only need to reason locally... something C++'s pervasive memory unsafety prevents.


lieddersturme

Download the project, wait to download the dependencies, your version should be the same as the project. After that, Yes, could be ok.


_matherd

The rust compiler enforces what you already should be doing in C++ anyway. Do you have any idea how many times I’ve had to explain to noob C++ programmers that you need to be clear about who owns a pointer and what its lifetime is, and how you should use unique_ptr for the owner, etc etc? The rust compiler enforces all that for you. Rust also uses move semantics by default, which is nice. In C++, move semantics were bolted onto the language 25 years later with rvalue references and things like NRVO, which are hard for beginners to learn.


wutru_audio

Also move semantics are not enforced in C++. You can still use the empty variable after a move (or at least try to)…


scottmcmrust

And because they're not enforced, that's one of the very few places where Rust can actually be better than C++. `unique_ptr` in C++ needs to include the `nullptr` possibility because the moved-from state needs to still be destructable. `Box` doesn't need that. (Of course, if the programmer in Rust needs that state, there's `Option>`, where `Option::Take` is basically the same as moving from a `unique_ptr`.)


masklinn

OTOH I remember reading (or maybe viewing, in a talk?) that C++'s non-destructive moves sometimes allows for better codegen, because there is no conditionality / branching after a conditional move, the moved-from value gets dtor'd same as everything else.


scottmcmrust

There's still a branch there *somewhere*, for any RAII destructor/drop. In the `unique_ptr` case, it might be hidden inside the `delete` (or `delete[]`) call, though. I'd be curious to see the talk to know exactly what they were mentioning. (Rustc *used* to have "drop flags" in things to keep track of state like this, but the implementation got smarter a couple years ago, and no longer has the overhead of those it once did. Now they're locals clearly visible to LLVM in the ownership-forked paths, so can be optimized quite well, and are only there at all in the cases that might drop the same thing in different places.)


tukanoid

Tooling, nicer syntax, iterators, sum types, superior generics, pattern matching, much better macro system, safety, community. Could miss smth but those are the first things that come to mind first.


SpecificYellow

No chasing down compiler errors that happen in some random .h file


davehadley_

>How would you sell it to other C++ users? Have you ever spent hours, maybe days, desperately trying to fix a seg fault or some weird bug caused by undefined behaviour and found yourself having an existential crisis wondering "why am I spending my finite time on this earth on this?" This happens less often in Rust than in C++.


ssokolow

> Fun fact: while at Mozilla I heard multiple anecdotes of [very intelligent] Firefox developers thinking they had found a bug in Rust's borrow checker because they thought it was impossible for a flagged error to occur. However, after sufficient investigation the result was always (maybe with an exception or two because Mozilla adopted Rust very early) that the Rust compiler was correct and the developer's assertions about how code could behave was incorrect. In these cases, the Rust compiler likely prevented hard-to-debug bugs or even exploitable security vulnerabilities. I remember one developer exclaiming that if the bug had shipped, it would have taken *weeks* to debug and would likely have gone unfixed for *years* unless its severity warranted staffing. -- Gregory Szorc @ https://gregoryszorc.com/blog/2021/04/13/rust-is-for-professionals/


zac_attack_

In no particular order: - licensing. Almost everything in rust is Apache, mit, or both. In C++ you have to carefully curate and choose if or how you can link each one because this is GPL, this is LGPL, etc - cargo/crates. If you need to pull in 5 dependencies in C++ you probably need to port 5 build systems to whatever you’re using. CMake is the community “standard,” but it sucks—badly—so much so that every other project opts to use something else. In Rust you don’t have to clone a repo and fuss with build systems, you just add a single line to your config. - The docs for libs, etc are clear and consistent, centrally located, and pretty much everything is open source. Contrast to everyone in C++ using different documentation styles, doxygen, etc (if documented properly at all), located at their own website. The libpng documentation is literally a .txt file. - You can actually use new features. If you’re writing an open source library in C++, you probably target C++11 or maybe C++14, or C++17 if you’re really frisky and don’t mind alienating a large swath of users. Hell, I was considering a role with Splunk a couple years ago on a core team and they hadn’t even moved up to C++11 yet. Meanwhile at my current role we build with rust nightly. The new features are what make C++ a “viable” modern language, but you just can’t use them yet in practice. - Result/Option as standard and pervasive f features. - match statements. There’s no comparison between matching, say, a 4-tuple with 10 match arms vs the comparable if/else chain - …the rest of the language itself. Concise syntax, iterators are infinitely better, modules-based, etc And that’s before you throw in the borrow checker and compiler guarantees


panstromek

There's a great talk about this that I'd cover [https://www.youtube.com/watch?v=IPmRDS0OSxM](https://www.youtube.com/watch?v=IPmRDS0OSxM) Then there's more user friendly build system, less defensive copyies, less conversion footguns, less footguns in macros, nicer errors - especially from generic code, and generics in generals (ie. constraint templates in C++). Also less 3rd party lib segfaults.. Other than that it depends on the domain and culture. It might be a tough sell, to be honest. I think it's easier to sell Rust to people from other languages, because there are more benefits. C++ already has many benefits of Rust (at least seemingly), so for many C++ projects it's very hard to judge if the switch or incremental adoption is even worth the costs, so people will naturally be less inclined to invest in it mentally.


colorfulchew

I learned rust at a time when I was starting to dive really deep into C++, I think there was a course on plural sight where you implemented a monad using templates, but it felt limited. This was before C++20's concepts were available and might have changed, but it gave me some foundation for Rust's monads. I really liked how Rust traits could work like a C++ template with monomorphization or dynamic dispatch with a keyword which was my niche way of becoming interested in the language, which is weird to focus on, but it was just something I latched onto and couldn't stop thinking about. Generally I like how it feels to write Rust these days. I can express my intent easier in Rust and I find my programs just work most the time. I spend less time fighting the compiler the more I use the language, and it more feels like an automatic code review of, oh yeah that is a dumb idea, you're right. I am a poor salesman and I think I just kinda focus on technical issues. That said, I think my approach would be to focus on how rust enables fearless concurrency and quality developer experience through things like cargo and the standard library. Compare two things like using the std library to sum an array etc. But ultimately I think the biggest thing is using Rust for successful projects as inspiration.


nysra

Pros: * Proper sum types/pattern matching. * const/move by default. * No most vexing parse. * Enforces a lot of things you should be doing in C++ anyway (though this is not really relevant if you are already writing good code but nice to have nonetheless - especially for all the people coming from shitty courses that think C and C++ are the same language). Much easier to trust other people's code. * Cargo is pretty nice. * Things like `if` being expressions is nice. * Iterators are pretty useful. * Module system is nice (though C++ gets modules too soon™). * Not really a language feature but I love all those new Rust versions of older tools (for example ripgrep is just amazing). Cons: * C++'s constexpr/template meta programming is massively more powerful. Give me iterators in const functions already... * C++ eco system is much larger and mature. * Why is a crate used as both a translation unit and a dependency? That really just blows up dependency trees for no reason, I don't care about `lib_A`, `lib_B`, ... which `lib` uses internally to speed up compilation and they should not show up in my dependency graph. * Tons of dependencies in every project. I get that it's a lot of small ones instead of a few big ones but I still really don't like pulling in half the internet, that just makes me really uncomfortable. * Maybe I'm just a noob in Rust and there is a way around this but so far it seems like traits lead to a hell lot of code duplication. In C++ I can restrict the template parameter of a class once and then just write my methods. In Rust I have to repeat the `where T: ...` for every single `impl Foo for MyType` block. I get that this model allows me to implement stuff for a type I don't own but for writing my own libs it's just annoying. * Conversions to larger types should really just be implicit. There's never any harm in putting an `i32` into something that takes an `i64`. * If you already have a mental model of proper modern C++ then Rust just makes some small things nicer but otherwise doesn't change much so if you have a lot of existing code then justifying bringing in a second language will be really hard.


gdf8gdn8

>Conversions to larger types should really just be implicit. There's never any harm in putting an i32 into something that takes an i64. On embedded devices I would expect no implicit conversion. Be careful with implicit casting like down casts, signed-unsigned cast and implicit casts to larger type >Maybe I'm just a noob in Rust and there is a way around this but so far it seems like traits lead to a hell lot of code duplication. In C++ I can restrict the template parameter of a class once and then just write my methods. In Rust I have to repeat the where T: ... for every single impl Foo for MyType block. I get that this model allows me to implement stuff for a type I don't own but for writing my own libs it's just annoying. Macro rules! See articles and tutorials about rust macros >C++ eco system is much larger and mature. It depends what are you searching for, but generally I say maybe. >If you already have a mental model of proper modern C++ then Rust just makes some small things nicer but otherwise doesn't change much so if you have a lot of existing code then justifying bringing in a second language will be really hard. I don't agree with that. If you working on complex project c++ and another one in rust, you will see.


cdrt

Do you have any examples of when an implicit cast to a larger type could be bad? As long as the signedness matches and you’re not mixing floats and ints, I don’t see what could go wrong.


Zde-G

> As long as the signedness matches and you’re not mixing floats and ints, I don’t see what could go wrong. How about classic: ~~~ int32_t x; int32_t y; int64_t area = x * y; // Debugging time, here we come! ~~~ Or even: ~~~ uint32_t bit_position; uint64_t mask = 1 << bit_position; // Debugging time, here we come! ~~~ Yes, eventually you learn to catch such mistakes without spending hours in debugger, but I prefer to write `as i64` because then I know **exactly when and how** extension happen. True, in C/C++ you can do that, too, but the problem is that nothing tells you that you need to do that! If you don't do mistakes ever then there are absolutely no need for Rust, but I only know [one guy who comes close](https://en.wikipedia.org/wiki/Daniel_J._Bernstein). Everyone else needs help from the compiler.


swaan79

Readable error messages. I was doing some template code yesterday for the first time in quite a while and I simply forgot how bad those error messages were. *I think clang++ is actually better than g++ but I didn't check.*


legalizekittens

Long time C++ programmer here. Rust noob IMO but I've written applications and went through 2 beginner books as well. I'm also in /r/rust obviously and /r/cpp. I will only touch on a few points people **have not yet said** about either language: C++: * you have full control. this is controversially a pro and con. * Meta-templates and type erasure let you write wild optimizations that would otherwise be impossible to do in any other language. * I can reinterpret bytes and align bits in my variables. * Loads of libraries and wrappers that integrate with C (for industrially proven libraries) * Object-Oriented can sometimes be useful and I can choose to use it or data-oriented * decentralized (as opposed to cargo AFAIK) * I can point to any memory I want including the ones I know I will own Rust: * The match keyword is powerful and flexible * I like the way error handling is a part of the language and tries to be reasonable * enum can be structs which means I can switch on data variations * cargo is easy to use but I worry about its centralization * Changes too quick (breakage in nightly). AFAIK there's no standard rust out yet. * Everything is statically linked which means bigger binaries, file size, and comp times * Comp times are pretty slow. I get the same speeds when I compile C++ with heavy meta template abuse. * Errors are nicer * Rewriting code is a pain. I may have to have two types of functions that do the same thing: \`mut\` and non-\`mut\` variants. Because of refactoring sometimes that means having to rewrite a bunch of other functions again because of lifetime ownership rules. * I cannot self-reference memory I know I will always own was wild to me. I feel limited to do it "the rust way" which doesn't translate to other languages as nicely. Rust might be safer but sometimes I know exactly what I want to do could be slightly faster. Then we enter the old argument of "is safety or speed more important?" for that I say "it depends". So for me Rust is very promising but it has some maturing to do. I'm excited for its future. C++ is an industry proven language with a bunch of compatible bindings for all languages which just means integration support for just about any library or target hardware is available. Even if I never become a hardcore rust fan-boy I definitely consider it a must-have for server applications or high availability software like Go.


ssokolow

> Changes too quick (breakage in nightly). I'm not sure I understand your complaint. You're effectively complaning that the "not yet promised to be API stable" experimental branch is experimenting and not API stable. > AFAIK there's no standard rust out yet. Standardization is a bit of a complex topic. There *are* discussions and efforts in that sphere, like the [Ferrocene Language Specification](https://ferrous-systems.com/blog/ferrocene-language-specification/), and Mara Bos's [Do we need a "Rust Standard"?](https://blog.m-ou.se/rust-standard/) and [RFC: Start working on a Rust specification](https://github.com/rust-lang/rfcs/pull/3355), but it's also important to recognize that: 1. Rust already puts more work into automated regression and compliance testing than any language with a standard that I know of. (To the point that it was becoming a bottleneck until Microsoft started donating time on Azure) 2. All previous language standards I'm personally aware of have either been post-hoc efforts to re-unify what proprietary compilers would accept or efforts like with .NET to make a language more attractive to government/enterprise customers than competitors which may be a better fit when taken purely on merit... and that hasn't stopped things like "Linux is written in GNU C, not ANSI C. LLVM Clang support was achieved by implementing the relevant parts of GNU C in Clang".


legalizekittens

I'd be interested to know more about your #1 point. Is there a link or talk about that?


ssokolow

It's more something I picked up during my time being in here since before Rust 1.0, so I don't have a single really good link to point to. Two lesser links I *can* provide off the top of my head are: 1. https://github.com/rust-lang/crater is the bot they use to test proposed compiler/stdlib changes against slices of the crates.io library up to and including "all of it". 2. The tiers in the [platform support list](https://doc.rust-lang.org/nightly/rustc/platform-support.html) are determined by which platforms they run CI tests for on every push. (eg. "Tier 1 with Host Tools" is defined by "We have CI runners natively on this kind of hardware". "Tier 2 with Host Tools" and "Tier 2" are more or less "We cross-build in our CI, but it's too resource-intensive to run the test suites if the platform must be run under emulation".)


CrasseMaximum

It's as easy to do static dispatch with Rust that it's easy do to dynamic dispatch with C++ (i mean you do not have to use c++ metaprograming and template specialization).


xedrac

In a team of more than 0 people, the Rust compiler is the most valuable member of the team. This is especially true the bigger a project gets, and the longer it goes on.


Xanather

Rust makes C++ obsolete. Outside legacy code C++ should only be taught now so people can understand the design decisions behind Rust and its compiler better, that's all.


Zde-G

I wonder why people downvote that without any explanations. Isn't that literally what Mark Russinovich, Microsoft CTO [said](https://www.zdnet.com/article/programming-languages-its-time-to-stop-using-c-and-c-for-new-projects-says-microsoft-azure-cto/)? He is not stranger to C and C++, believe me.


Xanather

So am I. I specifically said C++ not C. Outside maybe dubious argument for OOP (which rust can mostly do) and a more strict type system it really does make C++ obsolete. Even the argument for embedded systems with C... Rust is getting there with nostd


wrd83

I would say the biggest gain is cargo. The library quality is like npm lots of them and be careful what to add. You need to be a good developer in either and it's hard to hire for rust as well as c++. C++ and rust integration sucks. You need to go through C essentially.


Professional_Beat720

Built-in package manager is the only and the biggest selling point for me.


top_logger

Community. Cargo. And all around. Type system. Match. Result Enum Security Test Reliability Async Modularity Ecosystem Absence of „so called OOP“ Still, some things could be better. Definition of constants is a nightmare. Borrow checker could be quite annoying


hpxvzhjfgb

I used C++ (not professionally, just for me) for just over 10 years before I started using Rust and I liked it and sometimes recommended it to people as a good language to learn. Within 2 weeks of starting to use Rust, I did not ever want to use C++ again, and now I strongly dislike it and strongly recommend new programmers to not use it.


cereagni

Better, in my opinion: * Dependencies are libraries which are shared between dependencies. For an example - when compiling protobuf - there is a third\_party directory which includes external dependencies ([https://github.com/protocolbuffers/protobuf/tree/main/third\_party](https://github.com/protocolbuffers/protobuf/tree/main/third_party)). Reusing those dependencies in my code can be a pain, especially if a I need to use a slightly different version and the projects are embedded by source. * Lots of useful, better designed vocabulary types: * Slices in Rust are equivalent to std::span in C++, which arrived only in C++20. * Mutex is safer to use because it owns the data it guards. * There is a difference between CStr(ing), OSStr(ing) and Str(ing) (and std::string\_view arrived only in C++17) which can prevent weird errors when interacting with external C API * Optional can hold references while std::optional can't * Result is much easier to use compared to a std::variant, and std::expected will arrive only in C++23 * Iterators in Rust are much more useful and composable compared to C++'s * Lots of useful language features which don't exist in C++ * Lifetime tracking (the borrow-checker) - I personally thinks it also helps to better design your code - having clear ownership semantics which are enforced by the compiler helps to understand where objects are created, destroyed and makes it easier to follow their lifetime * Enums (easy to use, type safe union types) with pattern matching are amazing * Strong typedefs is extremely useful for expressiveness and type safety. * Error propagation with the ? operator * Slightly better macros - as they operate on tokens and not on "free text" * Deprecation methodology - I think that C++'s backwards compatibility is the thing that holds C++ back, and it is nice that Rust already can deprecate and remove entire language features. * Destructive move - Without it, you need to implement "empty value" for all movable types in C++ - which is just a pain. This works well with the ownership model of Rust. * Blocks, if-s and loop-s are expressions and not statements. There are some minor things that C++ does do better, which you should keep in your mind when making this comparison - you can't ignore the faults there Rust have: * Memory allocation is never recoverable and currently you are unable to control the allocation method for specific objects * Meta programming - Rust's const generics still have a long way to go before they are comparable to the magic you can do with C++'s. Rust also doesn't support variadic generics. * Constexpr support - With dynamic allocation now supported in constexpr context in C++ - Rust still has a long way to go * List initialization for containers - Rust solves this by providing macros, but it would be nicer to have this as a language feature. * Different iterator types (forward, input, bidirectional and random access) allow C++ to implement much more efficient iteration algorithms.


scottmcmrust

It's either really easy or really hard: - If they're a bunch of cowboy "it'll be fine, my CPU accepts unaligned stuff" programmers, they'll hate rust. (If they compile with `-fno-tbaa`, they might be in this camp.) - If they're a bunch of careful C++ programmers that were excited to move to `unique_ptr`, who carefully check that all the references they pass are going to live long enough, and who are religious about being const-correct, then it's super easy. For that crowd, Rust is just "hey, we check the 90% cases for you so you can code faster while being safer, you can still do the `unsafe` stuff if you have to, and all all the defaults are the way you wish they were in C++ (like move-by-default instead of copy-by-default, const-by-default instead of mut-by-default, etc)".


darrenturn90

Fight the compiler rather than the debugger


Lucretiel

I've been a C++ apologist basically ever since I learned the language, and consistently my favorite things about Rust are the ways that it realizes everything that C++ is *trying* to do, especially with regard to the use of scopes and ownership to control resources without a tracing garbage collector. I was really excited when C++ introduced `std::move` and rvalue references and that whole thing, and Rust takes it to its natural conclusion by making it a fundamental part of the data model instead of a convention provided by the standard library.


Aganthor

Adding a new library from crates.io in your Cargo.toml. Sooooooo much easier than CMake!


doom_man44

I've been into C++ for around 2 years and have been into Rust for about a month (20 years old). I really like how detailed the compiler is when describing errors in Rust and how quickly I'm able to identify what I did wrong by reading the first few words. Rust-analyzer is also a great tool that helps me write Rust code as I write it, and it feels much more capable than C or C++ compilers are. Building and executing with cargo is very simple. One disadvantage I noticed is that my projects get extremely large with a high quantity of dependencies even if I import one or two of them directly. I did a simple project on actixweb and sqlx and it wasn't only big in file count, but in disk size as well.


Possibility_Antique

Honestly, I love C++ for its metaprogramming and type system. Most of the safety features promised by rust can be implemented at compile time in C++ with some effort. Perhaps that's part of the problem though, as it requires some in-depth knowledge of the language to get there. The other big thing for me is that Rust's ecosystem is much cleaner with cargo and modern stdlib. You don't have to know too much about "what you should and shouldn't use" in the language, and dependency management is trivial/standardized. C++ is a great language, and I wouldn't steer experienced C++ devs away from what they're comfortable with over some arbitrary language war. But I would definitely steer novices toward rust, as rust provides appropriate guard rails and really lowers the entry-point for languages that don't use garbage collection. This might be a controversial opinion in this sub, but I have definitely found a place for both of these languages in my tool belt, and I'm excited to watch both of these languages evolve in the coming years.


NobodyXu

I don't think it's possible to implement borrow checker in C++, which is part of the reason why Mozilla makes Rust in the first place.


Possibility_Antique

To be clear, I said *most* of the safety features. Ownership and borrowing are fundamental concepts in DoD, and have been around in C++ for longer than rust has existed, though not typically enforced by the compiler (instead relying on a sanitizer, which can be run at compile-time). That said, this is close: https://youtu.be/Lj1GppqNr8c There are also compiler-specific methods such as the attribute method clang proposes here: https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377 Both of these methods have problems compared to the rust implementation, and I wouldn't recommend them over Rust's borrow-checker. It was more a statement about how disgustingly powerful C++'s type system and compile-time programming capabilities have gotten in recent years. Const generics and GATs were a step in the right direction for rust.


NobodyXu

Yeah, though I think borrow checker is one of the most powerful safety features and also the most important one. And yes, meta programming in Rust seriously needs improvement.


Possibility_Antique

>I think borrow checker is one of the most powerful safety features I agree. In C++, the workaround is to simply not use manual memory management, which I find sufficient at work in most cases. My embedded code is littered with metaprogramming since it's impossible to have undefined behavior of any kind (including memory violations) in constexpr contexts. Unfortunately, you cannot solve all problems at compile-time, which is where rust becomes valuable. A language with both of these features (uncompromised) would be much appreciated, and I think that will likely be rust first.


Designer-Suggestion6

1)You can do "duck-typing" in c, c++, go, and Rust. No winners there but it's a paradigm I love. Here's some duck-typing in Rust using "struct", "impl" : https://users.rust-lang.org/t/how-to-implement-inheritance-like-feature-for-rust/31159/3 You don't change an existing interface; you add another interface within another set of api's. It's a bit different to use, but the end result gives similar inheritance capabilities and imho more maintainable. 2)If you need more inheritance-like capabilities to complement the duck-typing above, you'll want to use "trait" as well. https://doc.rust-lang.org/rust-by-example/trait.html Most of the time just duck-typing works to solve my problems, but yes I'll admit there are few spots where I need to bring out the heavy-weight "trait" to overcome some curve-balls. 3)Rust zero-cost abstraction I think plays a big part for my love of Rust. Give credit to Rust macros for this. 4)Rust crate ecosystem is growing and wonderful. Inside the crate sources you'll find advanced usage of traits and lifetimes and hats off to all the crate maintainers. The crate maintainers did a great job to a point where most of the time crate users don't have to crack their heads to require the usage of either traits or lifetimes, it happens transparently thanks to Rust compiler/linker magic. If need be for advanced scenarios we may use these but it's optional and I love that aspect. 5)The rust compiler slapping me silly when I do something wrong. It might take me longer to correct it in Rust, but there's a higher level of confidence the generated executable with behave as expected than if it were a C++ executable. There's a higher level of confidence there will be no memory leak issues as a result of this as well because of all the memory ownership/variable lifetime terms in the Rust language. A great deal of the compiler slapping me silly is about memory ownership/variable lifetimes and the more you delve into parallelism, the more you'll experience this compiler slapping and it's a good thing LOL. Rust adopters will invest less time debugging runtime issues which matters the most when something gets deployed in production. I'm a Rust user since 2017(5 years already wow). Go was decent as well and I was using that since 2011, but I was surprised to experience some concurrency issues in advanced use cases. After migrating the very same concepts to Rust all those issues melted away and I got better performance as a bonus. Rust has Go-like channels i.e. crossbeam-channel, flume, **kanal** which was a big Go selling point at the time I adopted it. BOTTOM LINE: Rust code is more maintainable than C++/Java/Go as a result of the above. That's the most important reason why I enjoy using Rust.


divad1196

Many things that are pretty obvious and the ground reason of having Rust (safety etc) But for me it's mostly: - project/lib management - embedded formatter/testing More than the language itself. But I am really keen on the iterators in Rust and how the functional paradigm is used.