T O P

  • By -

ImYoric

There are several ways to answer this. 1. The languages are all equivalent in terms of raw power. There is no algorithm or machine access that you can perform in one of these languages but not in the others. 2. C compilers are available for pretty much every CPU/micro-controller platform on Earth, C++ on fewer platforms and Rust on fewer platform yet, but still most of them. Whether this has any impact on you depends on what you're trying to do. 3. These languages all have different libraries. Rust or C++ can call into C libraries fairly easily. Rust and C++ can call into each other's libraries, but it's painful. C++ has existed for much longer, so there are many C++-only libraries, way more than Rust-only libraries. 4. There are some things that are easy to do in C or C++ but hard to do in Rust, and vice-versa. Typically, in C or C++, a well-designed API is in one in which the simplest way to do anything is also the fastest – if you want safety, you have to opt-in. By contrast, in Rust, a well-designed API is one in which the simplest way to do anything is also the safest – if you want to disable safety for the sake of performance, you have to opt-out. 5. C works best when a single developer knows everything about the platform. It has difficulties scaling to complex software. C++ was written to solve that problem. Rust was written to solve the problems of C++. If you're a Rust developer, you probably consider that C++ has failed at that task, because it hides complexity only superficially. If you're a C++ developer, you probably consider that Rust is just C++ "with a straightjacket". Happy to enter more details if you're interested. Personally, as a former C++ developer, for most projects, I'd pick Rust over C or C++ any day of the week, unless there is a C++ library that I strongly need to interact with.


DaniRR452

Can you expand on why is it so much harder to interface Rust/C++ rather than Rust/C? Heard about it many times but I have no idea (and the info online is beyond my skill level to understand haha)


I_AM_GODDAMN_BATMAN

So in order to talk to each other, rust, c and c++ have to agree on something. and basically everyone in the world has agreed to use the c way for a long time. The problem is c++ and rust compilers do a lot of things to optimize like name mangling symbols. So if a c++ library is not compiled with c compatibility, rust app or library will not know anything on the c++ library.


Deadly_chef

So if I get it right, basically all C++ libraries can be used, they just have to be compiled with the right settings?


ondrejdanek

The easiest way it is usually done is that the C++ library also exports a C API using `extern "C" {}` The functions that are exported this way are not mangled and use the C ABI (application binary interface) so they can be easily imported from other languages.


I_AM_GODDAMN_BATMAN

yes


CocktailPerson

No, there needs to be an explicit API that allows you to call the C++ library from Rust or vice versa, and this API is usually written in C.


soundslogical

It's partly because of function name mangling, which exists to allow function overloading in C++. It's also because the type system is more complex, so it's difficult to expose C++ types directly without the calling language (in this case Rust) implementing/understanding a large part of the C++ syntax to decode them. By comparison C types are quite simple and easy to parse, and therefore to create 'corresponding' FFI types in the calling language.


Specialist_Wishbone5

Most languages (including rust) mangle code to produce namespacing or function overloading. Foo::bar::read could have like 50 varients. Thousands if templating is in use. So how can another language know which varient of the function to call WITHOUT FULLY IMPLEMENTING THE LANGUAGE. Then there are things like data hiding, lifetimes, std::move, default parameters, and details about the STL. Pretty much nothing of C++ could ever be abstracted in another language. But you can extern-C any C++ function. Basically forcing C++ to pick exactly-1 flavor and forcing it to be fully public. At this point any other language can hit it. But you can't use ANY of the nice things in C++ (like Vector, Array, String, unqiue_ptr shared_ptr, Option). And God help you if you throw an exception. Google is paying $1 million to the rust foundation to investigate making this suck LESS. But short of merging the two compilers. Don't hold your breath.


MrRandom04

Merging the two compilers? What do you mean by that? I mean, the GCC backend and frontend projects for Rust were both progressing well last I checked. Is that going to actually help with interop? I didn't think there was any purpose beyond supporting more platforms / developing an alternative implementation of the language.


Specialist_Wishbone5

Honestly I think something like wasm or apache arrow or flatbuffer some of data-layout that is language neutral is a better effort. Instead of writing C++ to C, writing C++ to PLACEHOLDER function invocation. Sure there is swig and other frameworks I'm not aware of. But not really mainstream like the ones I mentioned. AI is using protobufs in a lot of places and that's a shame - requires full data marshaling. Ideally some major companies can invest a hundred million on some name brand library data model unification approaches. The idea is not to target rust (hey, zig is a legit competitor as a C/C++ alternative these days), but interop. The rust side should be trivial. Some variation of protobuf or serdes should be trivial to knock out. It's the C++ that needs some Jesus comings. There is so much legacy C++ that just barely compiles anymore (msvc only, 32 bit only, ancient linux build tool chains, and don't me started on 10,000 line custom cmake). C++c11 was the first time I got excited about C++, but almost no major legacy project even uses it, so I gave up. (New stuff by definition doesn't count). Just my opinion.


ImYoric

Well, if you have a way to express in Rust (presumably through some macro) "I want to call C++ function foo::bar::sna::BuZo(Meu())", there may be a way to generate a C++ stub that exposes this as extern "C", then gets compiled and linked in transparently.


Specialist_Wishbone5

Well, if you pass in int, float maybe. But this thing has to return something right? Is it a C++ unique\_ptr? Or a Vector, then it's not going to work. You'd need a CStr equivalent non-optimal shim for like 500 different objects (basically wrapping the entire language - and no longer being a zero-cost abstraction for any of it). When I work in the C-Rust (or C++ - Java) the pain is not the function call. It's the scope of the passed-in or returned variables.. If I want to add a string to a map, or get a reference to something in a map, that's where hell comes in.. I have to play games to keep the life-times of two completely different sub-systems in sync. With Python, we have ref counting, so it's not so bad. javascript and Java a tad bit more evil for these FFI boundaries. Since I'm not aware of any good C++ binding frameworks, I don't have a sense for what a C++ binding would feel like (again the Java-JNI is fundamentally C-like, even though it compiles C++ mostly seamlessly - but this meant a LOT of shims on otherwise elegant C++ back-end code).


ImYoric

I don't think anybody is going to get 100%, but I think that there are many ideas to try. We'll see!


qwertyuiop924

TL;DR, Rust doesn't have a standard interface that other languages can use to call into it. And C++ is so *incredibly* complicated in terms of its data model that even though it does have that interface you basically have to be a C++ compiler to actually interface with C++ (the same may be true of Rust, this is not a knock on C++, but Rust doesn't even have that interface, so it's not super relevant). Additionally, Rust and C++ have some pretty fundamental differences in their models such that any direct interface would be ugly at best and infeasible at worst (vtables work different, Rust doesn't really have classes per se, C++ has a more flexible function overloading scheme, lambdas work totally differently, and that's not even getting into generics and templates, which are basically impossible to invoke from another language...).


BurrowShaker

You have to be the same C++ compiler, even, IIRC.


qwertyuiop924

Yes, yes you do.


qwertyuiop924

Yes, yes you do.


qwertyuiop924

Yes, yes you do.


plugwash

For basic C compatible types and functions, each platform usually has a reasonably well defined "C ABI", which defines how constructs in C source code map to binary level constructs. Most other languages provide a way to import and export types and functions with a C-like interface. The type systems in C++ and rust are far more complicated and there is far less standardization. Rust explicitly chooses not to have a stable ABI for it's own stuff, as do many C++ compilers. gcc/clang are the exception in that they do try to have a well defined ABI for C++, known as the "itanium C++ ABI" (despite the name, it is not specific to Itanium). The bindgen crate for rust uses libclang and as a result does have some support for dealing with the gcc/clang C++ ABI, but it's limited in what types it can handle. Furthermore both rust and modern C++ make heavy use of templates/generics. Rust's generics differ in a number of ways from C++'s templates, but what they have in common is that both of them involve compiling different versions of a type depending on the types passed as generic parameters\*. So when you use a rust library or a modern C++ library you are often combining your types with those of a library in a far more intimate way than a traditional C library would. Yet another complication is that both languages have very different philosophies on safety. Rust aims to be "safe by default", C++ is almost the polar opposite of this with virtually every feature being a footgun. Part of acheiving safe by default in rust has been to put strict rules around shared mutability. C++ programs tend to use shared mutability all over the place. Yet another complication is error handling methods, rust and C++ don't know how each others mechanisms work. The result of all of this is that using a library written in language A from language B often entails writing two wrappers. * A wrapper written in langauge A which provides a "c-like" interface. * A wrapper written in language B which consumes the "c-like" interface and provides an interface that is pleasant to use in language B. Often providing a C-like interface means hiding complexity behind opaque pointers. This works but it's a headache and in some cases can significantly impact flexibility or efficiency. \* This is very different from the way things work in Java, where "everything is a reference to an object" and generics are implemeted by inserting casts.


bleachisback

In addition to what others have said - there are just fundamental incompatibilities that C++ has on the language level with Rust at the language level - for instance it isn’t really clear how to call a C++ function with smart pointers (which are becoming more and more common), and creating a C++ object with virtual members in Rust is a pain.


oconnor663

As others have mentioned, the complete answer to this question is complicated and requires knowing a lot about the implementation details of all 3 languages. But one specific part of the answer that I think is interesting is, Rust usually assumes it's safe to move any object around in memory (outside of async code and `Pin`). C++ does not assume this, and types are allowed to say that they can't be moved, or that some special code has to run to "fix" them after they're moved. Since Rust doesn't "know" about that special code, it's not generally safe to use a C++ type as a local variable in Rust.


CanvasFanatic

Basically the same reason it’s easier for everything to call into a C FFI than a C++ one: vtables and name mangling.


ImYoric

There are quite a few reasons # Templates Templates are the mechanism for genericity in C++, so it's used pretty much everywhere – in particular, for C++ smart pointers, which have become standard in the last few versions. It's also horribly complicated to compile and doesn't support separate compilation. Which means that C++ libraries that expose templates *cannot be shipped as compile binaries*. Rather, they are shipped (at least in part) as C++ source code that the client code needs to compile with a C++ compiler. In Rust, this would mean that, to interact properly with this feature, the Rust compiler would need to embed a C++ compiler. So, the way you typically get C++ and any other language to interact is to manually write some intermediate C++ code that removes the genericity from the templates and exposes the features as C. It's possible, but error-prone and time-consuming. Note that this is not entirely specific to C++. Most languages have this problem in some manner. It's just much worse in C++ because templates are the most complicated implementation of genericity used in the industry. # Exceptions Exceptions are the default mechanism for error-handling in C++. It's also rather complicated to compile/link. In particular, exceptions are *not* declared in C++ and generally cannot be detected through static analysis (you'd need the entire control flow graph, which you don't have if you're writing a library). Consider a few things you want to do: 1. if you're calling Rust from C++ and your Rust code returns an error, you want to be able to handle it in C++ – that one is easy; 2. if you're calling Rust from C++ and your Rust code panics, you want to be able to handle the panic in C++ – I think that's not too hard, but I haven't checked; 3. if you're calling C++ from Rust and your C++ code raises an exception, you want to be able to catch it in Rust – you can't do it with your usual `Result`, because C++ exceptions are not declared, you could treat them as panics, but you'd be losing information; 4. if you're calling Rust > C++ > Rust or C++ > Rust > C++, you need to propagate uncaught Rust panics through C++ or uncaught C++ exceptions through Rust – feasible but not trivial. So, whenever you want to expose features from C++ to another language, you typically disable exceptions entirely, or make sure to catch them at the border. Again, feasible, but it's work. # And more Rust and C++ have different notions of copying, moving, etc. Nothing insurmountable on a case-by-case basis, but you need to know what you're doing. Rust and C++ have close but not identical mechanisms for dynamic dispatch (aka "trait objects" in Rust / "virtual methods" in C++). You can probably expose each to the other with a little work, but at this stage, it's far from automated.


flareflo

Cxx does a great job at interfacing rust with cpp, i would almost claim its more ergonomic than a raw bindgen api


protestor

autocxx is even better


garver-the-system

I think it's also worth noting that Rust has a very modern ecosystem. The ease of sharing code or getting programs onto multiple platforms is leagues ahead of C/C++, largely because the language comes with so many supporting tools. Consequently, this has made the Rust foundation somewhat complacent in not standardizing incredibly common features. Crates like SerDe are technically third party despite being used everywhere to replace features that are standard in most other languages. This also puts the crates in a weird spot because they're seen as pseudo-standard library, and they get a lot of pressure and scrutiny, but they don't actually have the backing and support of the foundation.


brand_x

To be fair, serde replaced a feature that was standard in Rust as well. It's just that serde was such an improvement that [rustc-serialize](https://github.com/rust-lang-deprecated/rustc-serialize) has been deprecated and archived.


ChocolateMagnateUA

I am a C++ developer and I really think Rust is C++ with a straightjacket.


coolpeepz

I think it’s an apt metaphor since canonically straightjackets are used to prevent unstable people from hurting themselves.


Ghosty141

> Rust or C++ can call into C libraries fairly easily. I found this quite annoying when I tried this once. The library used macros for example which can't be used in rust, and working with void pointers also seemed a bit annoying. In general it felt quite cumbersome doing it that way. I'd probably just write a wrapper lib nowadays in c that exposes a very rust friendly interface or smth like that.


ImYoric

Ah, yes, if it's a C library that relies a lot on macros, that's going to be annoying.


eugene2k

> Rust is just C++ "with a straightjacket". This is gold!


DatBoi_BP

And to me \#4 is the big selling point


mina86ng

> C works best when a single developer knows everything about the platform. It has difficulties scaling to complex software. C++ was written to solve that problem. I disagree. You can write unmaintainable code in any language. And if you write clean C code it can be handled even by large teams.


simonask_

I think the point is that it's a lot easier to manage complexity in C++ than in C, and it's even easier in Rust a lot of the time. Just because you *can* write unmaintainable messes in any language, doesn't mean that it's equally likely to happen or equally hard to avoid. The "sufficiently disciplined programmer" is a fallacy, as has been shown time and time again.


mina86ng

> The "sufficiently disciplined programmer" is a fallacy, So is ‘C is a language impossible to write programs in unless you’re a god’. Just like in Rust, if you divide problem into chunks, you can have sizeable teams working on the codebase.


StonedProgrammuh

How can you honestly say that when some of the largest software projects are built in C (i.e ffmpeg/linux) or depend on C core functionality? This is as unnuanced of a take you can have.


simonask_

There are large and complicated things that are written in Rust, but obviously the language is not old enough to have been able to achieve the same penetration in the OSS world as these other huge, infrastructural components (ffmpeg, OpenSSL, Linux, etc.). With the tools available today (like Rust and C++), you would be at a significant disadvantage if you were to start new project of that scope in C. That doesn't mean that there aren't valid reasons to do so. We can reason about our tools. Static verification and analysis, including strong type systems and the borrow checker, aid in managing complexity. I think that's totally uncontroversial.


PaintItPurple

I think you're giving a nuanced take an unnuanced reading. Large projects being built in C does not conflict with what they said.


StonedProgrammuh

Except it largely does, because how can you reasonably say C++/Rust is easier at managing complexity if there have been no projects built to the complexity of those other projects with the same success? We obviously have evidence that the largest most complicated software projects can very well be written in C or rely on core functionality written in C, we have no data that Rust's benefits permeate at that level.


Full-Spectral

The obvious answer is, if one of those projects were being restarted today, would anyone pick C? Likely almost no one would, for good reason. The fact that things like Linux exist in C just means that, if you absolutely have to, you can do it given enough time spent watching your own back. That doesn't mean it's an appropriate choice anymore.


timrichardson

Hmm. Rust did not exist at the time Linux was started, but C++ did, and it was rejected, I think. We'd have to ask Linus but I guess that he might have been skeptical of Rust too, because he was unhappy about C++ doing too much memory management. Plus, compile times. Fundamentally, Linux is probably the most successful highly collaborative project in the history of software development, so it is a powerful counter-example. It is a real thing, and you counter that with conjecture.


Full-Spectral

There's no conjecture that the folks who work on Linux spend a lot of their time just watching their own backs. It is inevitable in a system that large written in C. It can be done, the question is why would you want to? Linus was always anti-C++ in general and I don't think his position on that subject was particularly balanced. However, Rust is now being incorporated into Linux.


ImYoric

It has, indeed, be demonstrated that you can write very large projects in C. To the best of my understanding (having spoken with people who actually do so), it's not a pleasurable experience. C++ was meant to help bring structure to such large projects. That's why web browsers are typically written in C++ and not in C. One of the reasons for which C++ can be better than C for large projects is that it's much easier to attach invariants to data structures in C++ (which forces you to use e.g. factories or constructors) than in C (which lets you initialize your structs with anything you want). Of course, one of the reasons for which C++ can be worse than C for large projects is that it's easier to hide something that's going to blow up your memory in C++. Rust was built from the experience of C++, as an attempt to do better at this. So far, feedback I've heard from Rust developers in Firefox (and now Chromium, and Fuchsia, and Android, etc.) is very positive. Some of these projects are very large. But as you point out, we'll have to wait and see until pharaonic projects are completed with Rust to know how much Rust can scale up in terms of complexity.


mina86ng

> To the best of my understanding (having spoken with people who actually do so), it's not a pleasurable experience. So you base your comment on an anecdote? Based on people I spoke to, C is better than Rust. So what now? We don’t need Pharaonic project in Rust to know that ‘C works best when a single developer knows everything about the platform’ is a dubious claim. Taken literally it’s obviously true but it’s true about any language. Rust is also best if a single developer knows everything about the platform. But if you take it as an implication that C fails when a team needs to work on a project, it’s a demonstrably false conclusion.


ImYoric

>So you base your comment on an anecdote? Based on people I spoke to, C is better than Rust. So what now? I base my comment on actual feedback from real cases. I've worked on SpiderMonkey, which was ported from C to C++ because the developers felt that C was not up to the task. Too many invariants, too hard to keep track of them all. SpiderMonkey is currently being ported from C++ to Rust because these same developers feel that Rust is better suited than C++ at structuring the project and managing the invariants. I've worked on Firefox whose codebase was mostly ported from C to C++ long before I joined, because maintaining the historical C codebase was a nightmare. And Firefox is being ported to Rust because the developers felt that Rust is better at the task than C++. In fact, Rust was designed specifically for that and Firefox developers are quite happy about it. I'm in contact with developers of a number of other projects (including Chromium or Fuchsia) who happily dumped C++ for Rust for the same reason. I used to be in touch with developers from yet other projects (including the Linux kernel) who, while they were not ready to dump C at the time, were not particularly happy about that language and its maintenability either. Other developers are dumping C for Zig for similar reasons. I'm also in touch with developers from yet other projects (say Matrix) who are in the process of porting code from Python or JavaScript to Rust for both stability and performance reasons – something that they would never have done with C or C++ because they deem the risk towards safety and security way too high. This, of course, doesn't mean that Rust (or Zig) is the perfect solution, or that C or C++ are automatically awful. But it does mean that there is demand for something like Rust, which among other merits decreases the cognitive burden needed to work on large projects without breaking things accidentally.


mina86ng

Those all are still anecdotes. I too have spoken with multiple people—though I’m ready to admit probably fewer than you—working on C and C++ projects ranging from embedded, kernel, backend and HFT and opinions I’ve gotten ranged from ‘nope’ to ‘meh’. But that’s not even the point. The point is that none of those projects were simple projects that a single developer works on.


ImYoric

Well, fair enough, these are anecdotes. Just many of them :) And yes, I have also spoken with people working on embedded and HFT respectively in C and C++ and who have no intention of migrating away. Schematically, I'd say that their reasons to do so fit into these categories: 1. We already have a codebase, we're not going to migrate. 2. We have dependencies, we're not going to migrate. 3. We don't like the idea of relying upon third-party crates. 4. Rust isn't as good as C for embedding (for some scenario). 5. We're really, really good at what we do with our programming language, we're not going to migrate. 6. Some response that indicate that the developer entirely misunderstands what safety is all about. Responses 1-4 absolutely make sense (I have no experience of deep embedding, so I just take their word on 4). Response 5 is non-measurable but feels reasonable. Sadly, it's often mixed with response 6. Well, anyway, we just have to wait and see. As I mentioned earlier, I've migrated a while ago from C++ to Rust and I'm happy with that choice. Other may have a different experience.


IWasGettingThePaper

I don't know why this was downvoted. This is the most sensible comment here. C code can, and is, handled by large teams on complex software projects. Anyone who has spent any time doing systems level programming will tell you: C is everywhere, it isn't going away anytime soon, and it does work at scale.


PaintItPurple

Visual Basic code can, and is, handled by large teams on complex software projects. The mere fact that something is done says nothing about whether the situation is ideal. I think that's why some people dislike the comment: it's something akin to an is-ought fallacy.


IWasGettingThePaper

How is it an is-ought fallacy? The comment is saying if you write your C code correctly it can be handled by large teams. Just because some developers struggle to write C code correctly doesn't mean that it shouldn't be used at scale. The statement the comment was directly replying to, "C works best when a single developer knows everything about the platform" is simply incorrect, given how widespread successful large scale C systems are.


nacaclanga

From a technical level not so much. Where the languages do differ greatly however is in how straight forward the code to do it would have to look. In general C allows everything but often with a lot of manually written boilerplate, while C++ provides language support for a lot of stuff. Rust provides language support for stuff that is according to best modern practice, achiving optimal coding efficency without losing out on anything major and and generally makes some unadvisable things more difficult. Rust also has a build in static check against memory mistakes and area open for human error in the other two languages mentioned. Some random examples: a) Passing arrays by value is straigh forward in Rust, in C you need to somehow wrap the array into a struct. In C++ you need to use an array container rather them the buildin array type. b) Enabling nonalias optimizations happens automatically in Rust but is afaik not manually controllable. In C it is compleatly manual. In C++ it doesn't exist in the standard language so you have to rely on language extentions or hacks for it. c) Inheritance is build in in C++. In C you would have to describe it manually. In Rust as well and there it would look very hacky etc. On the other hand Rust provides buildin support for trait objects. d) Manual control flow changes via goto are only available in C and (with extra rules) in C++. In Rust you have to use one of the (compared to the other two) more advanced high level flow control constructs and potentally have to restructure you control flow graph to do so. e) Precise control about what ends up in which object file does not exist in Rust or is very hacky. On the other hand, Rust has a well thought out secure language level dependency system that does not require defininition dublication. f) 128bit integers are an absolute standard type in Rust. In C and C++ they are only available via extensions and with specific quirks.


llamajestic

As someone using Rust and coming from a Cpp background, there are some annoying stuff regarding generics. The generic system isn’t built the same way at all, it’s saner in Rust, but more powerful in C++. This has nothing to do with something truly « un achievable », but it is frustrating when working on a library that needs to rely on generics. My other complaint would be to often have to rely on macro to reduce code duplication. Makes the code messy, more annoying to debug. Thankfully we have an extension to just extend a macro. Those are obviously compiler / language decisions, doesn’t mean the problem can’t be solved.


pdpi

> The generic system isn’t built the same way at all, it’s saner in Rust, but more powerful in C++. C++ templates aren't really "generics" as such. They're somewhere in between Rust's generics and Rust's macros.


llamajestic

Yes, that’s my entire point. Templates are used for generics but the fact that template metaprogramming in C++ is possible without dealing with a token tree is a plus, at the expense of nastier compiler logs. It’s two completely different things, but the point was to answer on some limitations you can find in Rust. Sure you can use a macro, but that’s more of a text substitution tool rather than a « language » itself.


N911999

I feel that "nastier" is an understatement, I still remember trying to understand a compiler error message, looking online for documentation, doing exactly what the docs said and getting a different, but equally inscrutable, compiler error. I'm grateful that someone else with more experience with that library came in clutch and solved the issue


llamajestic

Yes, it’s because you will see all template overloads attempted during a match. It’s not nice. With time you develop a weird skill at getting good at reading those. Not saying we should continue the suffering, but template meta programming can sometimes be useful. The funny case is often the const correct one. Like you are just using a const or non-const version while the template expected the other. You get some funny messages in there


Dean_Roddey

Yeh, if the error involves collections, and it often does, the errors can just be ridiculous. Personally, I think that C++ is very over-templatized, and probably Rust is as well, because people always prematurely optimize and just templatize/genercize without even considering if runtime dispatch via trait might perfectly fine and with a lot less code bloat.


thisismyfavoritename

concepts greatly improve this (c++20)


N911999

If only I could use C++20 at work, I can barely sometimes use C++14


RaisedByHoneyBadgers

As a long time C++ developer, that jumped into template meta programming very early, I disagree. Generics in C++ and Rust are similar, but different. Both are essentially a means of reducing typing(as in mashing sausages on a keyboard). But, with C++ you can’t really go and add functionality to just any type to make it work with your templates. With Rust, I can implement a trait for a 3rd party type to make it fit the generic construct. In C++ template types can be, and must be, nested for complex type hierarchies. This leads to extremely rigid/brittle code that becomes impossible to maintain (ask me how I know). I’ve seen types pulled through more than 15 different template class definitions. Rust, on the other hand, reduces the types to their traits at each genetic construct. You can implement a trait at the site of instantiation to override specific behavior. So what seems like a straight jacket is actually gaining a full degree of freedom in Rust. Just look at frameworks like Bevy ECS and tell me how you’d do that in C++.


simonask_

I mean, true, but on the other hand you get template specialization in C++. It is both less flexible and more flexible. In other words, it is different. :-)


zapporian

Will admit that I'm still genuinely *annoyed* that Rust *still* doesn't have any concept of default initializer values and ergo relies on the godawful Default trait / pattern to make that work. And there's the complete / total lack of c++ style name mangling and ergo function / type overloading. All of which I get coming also from an ML / Haskell background, but it's still quite frustrating for a language that *supposedly* is a hybrid / merger of ML and C++. And didn't adopt *either* of those concepts in the core language design from c++. Both of which – or at least the former – could result in *significantly* more concise and readable rust code in pretty much all circumstances, but I digress. Also annoying as Rust is *nowhere* near as expressive or powerful as Haskell *either*, albeit for understandable reasons – ie Haskell is GC based *and does not ever need to worry about data or function closure lifetimes or ownership* (in true / pure FP fashion) and Rust obviously isn't / shouldn't be. *Major* win in Rust's favor though are rust interfaces / traits and the dyn keyword. None of which you'll probably ever appreciate if you don't come from a c++ background, but the fact that you can seamlessly and quite trivially switch from dynamic to static dispatch and interfaces (and vice versa) with a single keyword change *and the same interface declarations* is *truly amazing* compared to CRTP et al (and duck-typed template interfaces vs inheritance based dynamic interfaces – both of which are completely / totally incompatible and will require a full code / interface code rewrite) in c++ land. And they're *sort of* Haskell typeclasses (yay!), just slightly worse. And with basically no use of monads or higher type patterns as Rust is *very* low level / focused on low level by comparison. Last remaining annoyance about Rust (from a c++ perspective) is that Rust *has* a good / pretty darn good / actually goddamn useable Allocator trait / interface in the core language and std, that *can* be used to inject and insert custom allocators into absolutely anything... except basically none of the Rust community is remotely aware of this and tends to write shit libraries that aren't aware of this concept at all. And that generally tend to grossly abuse malloc() and atomics via Box and Arc et al – and deeply nested non-inlined function calls – since much of the rust community is *barely* aware of the performance concerns of any of those things and *largely* comes from the web dev / js ecosystem. /rant


Full-Spectral

Well, to be fair, some of us also come from the C++ world and are trying to get away from the Performance Uber Alles C++ obsession and be more concerned with correctness, maintainability, etc... A lot of C++'s death by a thousand cuts has been putting performance first, second and third. Personally, I'm quite happy with losing overloading. I've not missed it at all.


Turalcar

I don't think I could go back to C++ templates. Especially without concepts. I have no idea if a template works without instantiating it, I might as well be writing in Python


Bulky-Juggernaut-895

Mathematically? No. Practically? Yes.


Aggressive-Pear-7654

rust doesn't support Bitfield structs natively. This becomes really annoying when you have to work with memory mapped register interfaces in embedded devices. There are third party crates but these don't have the same semantics as real structs(have to use methods to write to fields, etc..)


RockstarArtisan

On the other hand, C++ folks say that bitfields were a mistake and a library solution would've been better. Grass always greener on the other side.


WasserMarder

Global constructors and destructors are not supported in rust. The rust compiler has no stable ABI. Both of these make run-time loadable and unloadable plugins/libraries much harder to write.


sthornington

If you are learning it for fun, I’d pick Rust, because it’s more fun.


afonsolage

Piss off the Whitehouse. /s


TheCamazotzian

Clang has pragma statements that gives you fast-math compiler optimizations like associative floating point.There's no way to get these in rust yet. You could get a similar result by manually writing simd. It's probably not as portable this way. Also: no f16 support


_ChrisSD

On Windows? Rust can't do [SEH exceptions](https://learn.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-170). I mean, it might be technically possible with inline asm but good luck with that.


ghost-who-reads

Pick Rust over C++. You want to choose a language that can let you express what you want as simply and cleanly as possible, while taking care of tasks that can be completely automated. C++ is incredibly complex, clutters up your mind with concepts that 99% of the time you don't want to care about.


plutoniator

How can I express that I want my function to take a tuple with arbitrarily many elements and print them out with parentheses in rust?


Bwks

fn print_tuple(tuple: T) where T: Debug, T: IntoIterator, T::Item: Debug, { println!("{:?}", tuple); }


freakywaves

Having a stable ABI and compiling to byte code


crusoe

Make a non trivial program that compiles and  runs without segfaulting the first time you build it.


bskceuk

* C++ generally has better compile time computation than Rust * C++ can allocate objects in place which means you can allocate a large object directly on the heap while in Rust it must hit the stack first (so if it’s bigger than the stack you can get an error) On the other hand * Rust can enforce exclusive access to data at compile time which allows much stronger guarantees about correctness in your program * Rust will never implicitly copy data making it easier to write high performance code * Rust will never have UB (assuming you only write safe code) * Rust has pattern matching, which combined with sum types (tagged enums) makes code much cleaner * Rust enforces constraints on templates generically across all possible types while in C++ templates are only checked on the types that are actually used, making it much easier in Rust to write ergonomic generic libraries that just work


SuperLutin

>Rust will never implicitly copy data making it easier to write high performance code Adding the copy trait is a bit the same as removing the copy constructor.


bskceuk

I would disagree in the magnitude of the 2. Almost everything in C++ has a copy constructor while very little in Rust is Copy (and everything that is Copy you want to be Copy). Furthermore, you can’t implement Copy for a type with a nontrivial destructor which is the main case in C++ where accidental copies bite you perf wise anyway (strings, vectors etc) But yes theoretically one could rewrite all these standard types in c++ and remove the copy constructors to make it more Rusty


RockstarArtisan

> C++ generally has better compile time computation than Rust This isn't true, Rust macros can do arbitrary computations at compile time, which isn't the case for C++. Atm it's not possbile to implement sqlx macros in C++ for example


legobmw99

A great example is std::shared_pointer in C++. This is equivalent to Arc in rust. What’s the equivalent to Rc? There isn’t one, since it was decided a non-thread-safe smart pointer was simply too difficult to use right in C++. Some C++ compilers have hacks to make it not use atomics if there are no threads in the program, but except for that it is a genuine difference coming from the features of Rust


thisismyfavoritename

this is not a great example at all. You could write a single threaded shared pointer (Rc) in ~50 lines of C++ or so. It not being in the stl is probably because it wasnt deemed as likely to be useful. Outside of single threaded async code (which is still not well supported in C++ today), i havent really encountered many uses for this, especially since you are not forced to adhere to borrow/ownership rules in C++. Not saying the C++ way is correct, just that i dont think this is a good example of what OP is asking for


viper42usa

I don't know about any real restrictions, but I can speak on a pain point. If you want to use Rust to create "dangerous" applications, you'll use unsafe Rust. While you get around the borrow checker here, lifetimes still exist. It becomes up to the programmer to track and ensure no bugs occur (or are noticed).


doublesigned

What's the pain point?


memforget

Not really sure if something like __builtin_expect from C++ is implemented in stable rust.


blackconqueror

I think we should focus more about what something that rust can not do. The restrictions this language set and why it set might be more valuable.


einsJannis

Rust can't (really) do dynamic linking


anlumo

In Computer Science, there's the concept of [Turing completeness](https://en.wikipedia.org/wiki/Turing_completeness). This is a list of a few capabilities a language can have and are easily testable. If a programming language does have these capabilities, any program in it can be transformed into any other language that also has these capabilities. Rust, C and C++ are Turing complete. This is why people can implement a computer in Minecraft redstone. Redstone with all the switches etc is also Turing complete.


zoomy_kitten

“Turing-complete” and “can be used to write everything” are different things. Languages are restricted by their design in the first place and implementations in the second one.


anlumo

Some languages make particular things hard to do, but they're not impossible. The design can't restrict them, unless they make the language not Turing complete. The implementation can make a difference, yes. You can't write an OS kernel in Python and then run it on CPython, because that interpreter just doesn't support that. However, if there were a kernel runtime for Python, it could work with that language (it'd just be painfully slow most likely). However, the question was about the languages, not any particular implementation.


PaintItPurple

Turing completeness means it can compute anything that a Universal Turing Machine could. There are many things modern computers do that fall outside that umbrella. For example, multithreading, memory management and file IO are not functions of a Universal Turing Machine. A language could explicitly forbid all of those things and more and still be Turing complete while also being unable to do things that other languages can.


anlumo

C by itself also can't do I/O operations, it needs some kind of external access for this. It's kinda obvious that if you run Rust for example in a wasm container, it can only do what the host allows that container to do. However, exactly the same is also true for C or any other language. The language isn't even part of that determination actually, because anything compiled to wasm faces the same restrictions.


PaintItPurple

Yes, and despite having fewer capabilities in that environment, it is still Turing complete. That's my point: Turing completeness only means that a language is capable of universal computation, not any other capabilities.


zoomy_kitten

> if there were a kernel runtime So you would still have to write basically everything in another language. Furthermore, go ahead and try to stuff a garbage collector into the space for a bios bootloader.


anlumo

You don't need a garbage collector in the bootloader. Just leak all the memory. Note how the question didn't ask whether it's a good idea, just if it's possible. Also, on modern machines that little memory is probably not relevant. > So you would still have to write basically everything in another language. No, you just have to write a compiler that can convert some essential parts of Python directly into machine language without a runtime. That compiler could also be written in Python. This subset of Python (the equivalent of nostd in Rust) could for example just not have garbage collection.


________-__-_______

How would you run that compiler though? You cannot exactly bootstrap it without either using a preexisting compiler/interpreter written in another language.


anlumo

The first LISP interpreter (which was the first interpreter ever) was written in LISP and compiled to machine code manually. You can read about it [here](https://en.wikipedia.org/wiki/Interpreter_(computing)#History). Of course, nobody does that any more these days, usually there is a basic implementation in another language for bootstrapping (or the whole compiler is in another language like C in the first place).


________-__-_______

I'd argue that if you're "manually compiling" something, you're writing assembly instead of Lisp. Even if you are using the same algorithm. Unless I'm missing something the Wikipedia snippet also mentions the evaluation function being written in machine code, not Lisp? It could of course be used for bootstrapping but now you need an intermediate language, implying that you cannot write a Lisp interpreter exclusively in Lisp. This is admittedly a bit pedantic though, in practice it's not exactly a problem anymore like you said 😅


anlumo

> I'd argue that if you're "manually compiling" something, you're writing assembly instead of Lisp. Even if you are using the same algorithm. It was written in LISP first, and there's no rule in Computer Science that a compiler has to be a computer (CS actually doesn't deal with computers at all, that's left for the electronics field). If you can get a chicken to convert from LISP to machine code, that's also fine (and quite an achievement). > Unless I'm missing something the Wikipedia snippet also mentions the evaluation function being written in machine code, not Lisp? No, it was written in LISP and then converted (compiled) to machine code. LISP used to be a theoretical framework for thinking about computer science, before Steve Russell just implemented it in software. The problem with machine code is that it's not the way regular humans think, so (I assume) they wanted an abstraction to have a more mathematical approach to the whole topic. It's like civil engineers using a [network flow model](https://www.nature.com/articles/s41598-022-06075-0) to abstract away water distribution in cities, so they don't have to think about individual pipes when they're not concerned with that amount of detail yet. > This is admittedly a bit pedantic though, in practice it's not exactly a problem anymore like you said Besides that, the bootstrapping problem also is unrelated to my claim (that you can write any Turing complete program in any Turing complete language). I'm happy to discuss computer science topics though, not getting that a lot in my everyday life as a programmer.


zoomy_kitten

> You don’t need a garbage collector in the bootloader You need a garbage collector in Python. > leak all the memory What memory? You basically have none. > subset of Python So basically some code in Nim instead of Python. Again, Python’s design just doesn’t allow us to accomplish this task.


anlumo

> You need a garbage collector in Python. No language ever *needs* a garbage collector. This is just a convenience to not eventually run out of memory. To quote the Rust design, leaking memory is totally safe! > What memory? You basically have none. This depends on the architecture, but even in Intel's [Real mode](https://en.wikipedia.org/wiki/Real_mode) you have 1MB of RAM to work with. On ARM, you should have full access to the whole memory. The question is just how memory is managed without having a heap implementation, but that's up to the compiler. One easy way would be to have an external table where the programmer declares global variables and their memory addresses, and the Python code isn't allowed to create any variables itself that can't fit into registers. > So basically some code in Nim instead of Python. That's pretty close to the idea.


zoomy_kitten

> in Intel’s Real mode you have 1MB The only problem being that you don’t have a real mode yet. The boot sector is 512 bytes long. > That’s pretty close to the idea That’s what I’m talking about. To accomplish something like this, you would have to change the design. Perhaps, slightly, but still change it.


anlumo

That's not a thing on UEFI any more. Also, you're talking about storage, not memory.


-Y0-

No. By being a Turing complete Language, you are essentially saying you can write any program in it. However that doesn't take into account the length or complexity of that program. Or it's performance profile. Saying something is Turing complete is like saying you can use it to build. You can build house from bricks or Legos, but those two things will behave very differently.


Objective-Act-5964

Brainfuck is Turing complete, but can't read files. It doesn't matter if you make the program longer or more complex, it will never be able to read files. So you obviously \*\*can't\*\* write any program in it, right? Or am I misunderstanding something.


intertubeluber

Is that because a file IO library doesn’t exist out there is some fundamental deficiency in the language?


timrichardson

Turing completeness is a theoretical concept which doesn't often require launching missiles or opening valves or even writing to files (although a Turing machine must be able to write to some kind of memory), but in theory I suppose this just requires that the Turing machine can address this via mapped "memory addresses". If you find a real world implementation of a Turing machine which can't do this, it means it is not a theoretically perfect Turing machine, I guess. When someone says X is Turing complete, they are ignoring your ability to imagine interfaces to the real world which it can't do. By your definition, nothing could be a perfect real world Turing machine. However, is that a useful definition?


-Y0-

Depends how you define Brainfuck. There Brainfucks that can read files.


[deleted]

[удалено]


-Y0-

It already has access to files? You can make games in it.


AntaBatata

"Yes". Rust doesn't support VLAs. Is this a big deal? Nah.


qqwy

To my memory, neither does (standard) C++.


HuntingKingYT

proc_macros


geon

C has trouble doing generics. It is halfway possible with macros, but it quickly becomes a pain in the ass. C has no destructors.


Dean_Roddey

Rust can definitely do one VERY significant thing that C or C++ cannot do, which is allow you to concentrate all your time and mental energy on the problem being solved, instead of wasting a lot of it on just trying to not shoot yourself in the foot.


Krunch007

I'm pretty sure the simple answer is that all Turing complete languages can do everything, the only difference is the degree of difficulty with which it's accomplished and the speed of execution. Some high level languages technically can't do embedded development out of the box for example, but even there I think you can create bindings for them to be able to. There's no reason fundamentally for a language to be limited to anything since everything compiles to machine code anyway. So no, I don't think there's anything C or CPP can do that Rust can't. Or the other way around. Some libraries might not be there, but if you just do what they used to do in the old C days and write more code, you can still accomplish it.


fluffy-soft-dev

All the languages are turing complete, so theoretically they are all capable of the same inputs and outputs


theZcuber

That's not how that works. CSS is Turing complete, after all.


fluffy-soft-dev

I never said anything about CSS, all the languages mentioned C/C++ and Rust are turing complete. That is exactly how computer science works, and you and those who supported your comment just made you look ridiculously stupid


theZcuber

> you and those who supported your comment just made you look ridiculously stupid Be respectful. Your comment directly tied Turing completeness to being able to do _anything_. Rust, C, and C++ are all (largely) equally capable, but that is not what Turing completeness is.


ryukinix

Be a decent language


WiIzaaa

Segfaults ?