Ehhh the reason why `std::unordered_map` is slower than Abseil's flat hash map is because it makes certain guarantees that the Abseil hash map doesn't make. There is no one-size fits all solution for containers unfortunately, only trade-offs. If you have really strict performance demands, you're going to have to find an implementation that makes the right trade-offs for your application.
@@Erryjet ikr... this video was basically comparing the result of using completely different implementations of a hashmap, and not the difference between the languages. I also love how at first he said that the C program was easier to write because you had to tell the Rust compiler exactly what you wanted, and then at the end he says that all of the examples he's show somehow prove the rapidity of development in rust and i cant help but be completely baffled at the pointlessness of this video.
I would say, std::unordered_map optimized for very-very rare scenario. Open addressed hashmaps are better suited for the main purpose of hash map: associative arrays.
A professor of mine also remarked in a recent C++ lecture how he was wrong in the past for using vectors for everything, which would cause hiccups in certain scenarios. Learning the differences and usecases for all containers is tedious, but rewarding.
The first example is great to illustrate in what way Rust forces you to get things right, but in reality this is something only a beginner would likely have a problem with. Once you get used to enough Rust (use it for two weeks or something), you pick up certain patterns, such as using &str (string slice) anywhere you need to use a string, unless in specific cases where you need to own one, for instance when initializing a struct. And in fact, using &str instead of String in this case makes it _actually_ equivalent to the C version where a char* was used (almost, since Rust works with utf8-checked string slices which have a length instead of zero-terminated strings, but that's just a detail).
Yeah great point, another commenter said something similar and I agree. Definitely most prevalent among the beginner / average programmer, or even an experienced programmer who is just a beginner at Rust. Also I did not know that about Rust string slices vs. C char pointers. Thank you for sharing!
Well, that detail, having UTF-8 by default in a programming language, is a really great one. And it's one of those things in which the language does not only do a greater job than C and C++, but also compared to some higher level languages like Java and C#, where UTF-8 isn't the default either (those languages use Unicode UTF-16 by default in memory, UTF-8 is only the default for things like file IO operations).
@@jongeduard well most Windows and Mac OS X APIs use UTF-16 unicode internally, possibly forcing a conversion from UTF-8 to UTF-16 when calling those with these strings, that's also why QT choiced QString's implementation to use UTF-16 strings, but yeah it would be cool if they had native UTF-8 support , i saw some people talking about windows having new UTF-8 support but from what i searched it seems like that supports is mostly converting your UTF-8 string to UTF-16 which is still slower
"Rust is faster, because I'm using a third party component that is faster" Don't get me wrong, I get your point and the first example is pretty good, but the second one doesn't prove any point. C++'s implementation of the STL (or other stdlibs) is at most *suggested* by the standard committee, never implemented. The compiler / environment developers can choose what algorithms to actually use to implement them. I'm certain there is C++ libs that use Abseil's hashmap implementation for std::unordered_map. Also, C++ is not dying nor obsolete, since very useful revisions and redesigns are happening (c++17, c++20, c++23, ...)
There are basically two points that you seem to be missing, both relating to composability: 1. Rust's strong encapsulation guarantees at the API level and unwillingness to rush into having a stable ABI mean that the Rust standard library can and has already had its stuff replaced with faster implementations, while C++ is stuck with slow implementations of various things that can't be upgraded without ABI breaks. (See "The Day The Standard Library Died" by cor3ntin, for example.) 2. As Bryan Cantrill says in the segment at 40:53 in "Is It Time to Rewrite the Operating System in Rust?", those guarantees make people more willing to engage in more aggressive optimizations. He's talking in the context of C, but C++'s decisions to remain so close to C for FFI compatibility, and the resultant memory unsafety, mean that it has similar problems. (eg. I've heard various examples of developers adopting "when in doubt, make a copy" policies around std::string and std::string_view in C++ for lack of the guarantees Rust's borrow checker provides surrounding string slices.) To quote the most important part of that Bryan Cantill bit I mentioned: "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." Rust isn't just about memory safety, it's about a pervasive effort to ensure that you only need to reason locally about code's behaviour.
@@BGFutureBG Chandler Carruth has a number of talks at CppCon on UA-cam that discuss the problems with the stdlib in C++. The APIs themselves require certain implementation details that are incompatible with the optimizations seen in the abseil flat hash map and Rust. A vendor cannot use this in their C++ standard library while also being compliant. That said, abseil is public, so it's not like you can't get the thing in C++. It's just a shame that the default option in C++ is often not very good. I'm a C++ Dev that has never used Rust, just annoyed by the language being unexpectedly slow for even simple tasks at times.
I'm pretty sure C++ standard doesn't specify what algorithm should be used for a hashmap and afaik the implementation in GNU libstdc++ is faster than the one in Rust EDIT: I just tested it with 5000000 insertions, this is the time: C++ (g++): 309 ms C++ (clang): 249.406 milliseconds Rust (rustc): 394 milliseconds
You'll come to realise that it no longer becomes fighting the compiler, it truly starts becoming a like a peer programmer helping you out always making sure you write safe code
You have the choice to fight with the compiler or with the code docs, server errors and clients. Do you want to fight against one big army now or 1000s of small ambushes in the next 5 years?
@@wumwum42 Mostly this. You will learn to write compiling code faster on the first try or in not that much time. Languages like C will still compile if the program is incorrect and interpreted languages don't have that check at all and you **will** get unforeseen runtime errors in production. Unit tests can only catch these bugs if you make one that tests for that bug for each function. rustc does this before it compiles your tests. It's so much faster in that sense, even python can't hold a candle to this. How fast can you make that application? For python that may be faster than rust in general, by maybe a factor 2. How fast can you make it correctly? Rust will certainly win this one with a far bigger margin.
In the first example, the Rust code is equivalent line by line to the C code, the only reason you'd have issues writing is if you simply don't know the language. The same would then be applicable to C: if you've never used printf before, it's likely going to take you some time to figure it out. In the divide example, again: The rust code is basically equivalent to the C code, because in C you would mark the pointer as const if the function doesn't need to write to it. It's a convention that I have always seen used everywhere, so it isn't really that ambiguous. As for the return type, if you really wanted to make that clear, a simple typedef would allow you to change the int into something more explicit. As for the std::unordered_map, you can provide your own hash class to make it faster. I have seen benchmarks in which C++ absolutely crushes Rust, depending on the provided hash function. That said, the choice of said hash function is still up to the programmer, so the code will be a bit harder to write. Generally speaking I would say C does indeed require more time to write an equivalent amount of code, simply because you have to do a lot of work yourself. As for C++, I'm not convinced this is the case. By the way, C++ is faaaaaar from being in the same state as Cobol, it is still an actively developed langage, and is to this day still very widely used in the software industry, unlike Cobol which you will only encounter in very specific codebases, purely for legacy reasons. Take a look at job offerings for C++, Rust, and Cobol, I'm pretty certain the first one will give you way more results than the other two.
UA-cam recommended this video to me again, and watching it a second time just shows more of the flaws in logic: Twitter developers re-writing something in Rust faster than refactoring C code doesn't tell us anything about whether the C code was good, readable, or idiomatic - It could, but it is more likely just telling us that these particular devs didn't understand it as well as they understood Rust. Then the video switches to "well the default hashmap library in rust is faster" without discussing why that is so - hashing algorithms can be as specific or generic as you can make them, and it is all about tradeoffs - maybe "cody's random hashmap" library is actually better for a particular use case than the standard Rust hashmap, but there is a performance cost that is worth it in the long run - the only way you'd know is reading the code or the documentation to find out - which is the same thing regardless of whether you use Rust or C for your implementation.
Both examples of when coding can be hard seem to just be skill issues. In the Rust example, one would only write that code if they didn’t know how Rust memory management works. In the C example, one would only use those hash implementations if they didn’t know how their libraries worked. In either case there’s a steep learning curve; Rust is hard to learn and get used to, and a bad library is hard to understand. Difficulty to develop something is usually a function of developer skill and understanding of the codebase. Although on the subject of libraries, that comparison can fall apart depending on the libraries, and one would be ill advised to assume that Rust’s standard library will always be faster than an external library. In general, I don’t think claiming that Rust is faster than C is a great strategy. At the language level, C is still faster, and any algorithm that can be implemented in Rust can be implemented in C. Performance is part of Rust’s appeal, but its main selling point is memory management. Rust is a bit slower than C, but it’s still faster than most languages and in return you have guaranteed memory safety.
Interesting, that is very true. Thank you for sharing this, these are great points. To your point I would say that although you could do anything in C that you could in Rust, if memory safety is an issue it would probably be much faster (depending on the content and size of the project) to do it in Rust. If not an issue, definitely C would be the easier and faster (both in performance and productivity) choice. I should have been more clear about this in the video. Thanks again
I agree with your comments. This video felt more "pro-Rust" to me than an actual comparison. I mean look at the title of the video. Your point about skills is spot on. I'm very interested in learning more about Rust and I think it's fantastic, but I also think it's critical to keep comparisons like this as objective as possible to avoid sounding "fanboy-ish".
Rust is a bit slower than C *on the benchmarks game*. Real-world situations keep showing Rust implementations to be faster because teams are willing to engage in more aggressive optimizations and are able to reuse more code written by skilled third parties. Listen to the segment of Bryan Cantrill's "Is It Time to Rewrite the Operating System in Rust?" at 40:53. The key part is this: "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."
@@ssokolow You make some great points, but they really point out @mattpattok3837's point. Those are "skills" issues. I would argue in favor of Rust because there just aren't as many people as "skilled" in C as there are in other languages, these days. "gnarly" programming can be a desirable challenge to some and something to completely avoid to others. Hence the interest in using third party libraries for so many things, these days. With that being stated, I certainly do understand the productivity arguments. One could argue programming in Rust can be far more productive because you're not spending as much time focusing on as many programming details, as you would have to if programming in C.
@@TheCocoaDaddy Skill does play a part but empirical evidence seems to show that it becomes exponentially more difficult for humans working in groups to write correct C or C++ as group and project size increase. Personally, I'd see it less as "not enough skilled coders" and more as "not enough reckless coders".
Conversations with Rust users: Newbie - "God this code takes ages to write feels unnecessarily verbose" Professional - "ACTUALLY THAT'S A GOOD THING YOU'RE JUST A BAD PROGRAMMER RUST IS FLAWLESS" Conversations with C/C++ users: Newbie - "God this fucking language is horrible and I'm in immense pain" Professional - "yep" Just to clarify, I genuinely think Rust is probably a better language than C/C++ and I'm all for its widespread adoption, but holy shit if I don't find people constantly singing its praises annoying
You don't say. The evangelism is real with this language. I wish we could remove all the prejudices and the attachments and the emotion and just argue honestly whether something is "always" better or whether there are some trade-offs we need to consider in all these circumstances. As Linus said, religious overtones. We ought to move to programming atheism instead.
3:40 In bigger coding projects you should always specify function parameters as they are intended to be used. For example making param1 in your div() function a pointer to a constant value. int div(const int *param1, ...) if it is read-only
I think you gave an unfair example for c/c++. Though yes variables are mutable by default the const keyword would have fixed the problem of not knowing if the input is modified or not. I believe that it’s just easier to write bad c/c++
Agree that const solves many things in C++. Still, there are problems with lifetimes that the compiler doesn't even warn about that can give anwhere from wrong result to segfaults / other memory errors. I hear that Rust has solved that sort of thing. I need to learn about it.
This will be the case in 100% of these "Rust is faster than C" videos. The purpose of them is clickbait to supply for mental-masturbation for Rust enthusiasts. I like Rust a lot, but it is important to stay grounded in reality, and not let one's preference of language be such a bias and defy logic.
I'm not sure I dig this argument of Rust having better defaults as an explanation to why it is faster. In this specific case it is, but in others it can be different. I remember being unable to match some very simple Go program's performance and having to dig into Google's baked-in implementations to see what they were doing differently from Rust's std library. And at the end of the day I didn't manage to do it still. Sometimes it will be faster, sometimes it won't. However, the one advantage that Rust has regarding speed is zero-cost abstractions, and especially its very well optimized iterators which compile down to for loops and if statements.
@@jh29a I'm one of his patrons lol, but it wasn't copied, I just remember he said that and find that it is the best way to explain why iterators are fast xd
Wow, great video. I'm stunned it's the first video on your channel. Very informative and very well presented. That's it, tomorrow I'm starting a project to learn Rust (this time I mean it :D) Subscribed, obviously.
Yeah there's clearly slot of inspiration from No Boilerplate's and Fireship's UA-cam channel (which is a good thing BTW) No Boilerplate - youtube.com/@NoBoilerplate Fireship - youtube.com/@Fireship
I don't know why UA-cam keeps recommending me Rust videos when I prefer C/C++ more. Guess I'll have to manually start telling UA-cam to not recommend Rust to me.
@DiadeTedio Tedio Well, I decided to check it out, knowing that it has a bit of a cult fanbase, which initially drove me off. It was a painful experience because the compiler wants you to do things in a specific way, and anything different than that is an error. Basically, it doesn't allow me to do things that C or C++ would normally allow. I don't care about memory safety if I can't do things my own way.
@@_M_643 I would say C++ is much more "cultish" than Rust by a larger ammount, but this is just myself talking, because I'm in contact with the C++ community longer than I'm in contact with the Rust community, but ok. For the next, have you tried Go? It is safer than C or C++, relatively fast and has some of this "freedom" to do different things, maybe it would be good for you to learn hard some other languages, just for fun and because learning is almost all good. Maybe you just don't like Rust, but if you recognize the need of the language when safety a concern, that's fine!
4:40 The compiler gets angry with you if a variable is marked mut but isn’t changed, so unless the person writing the code is being purposefully deceptive, you can be sure that it will be changed. And when you try to compile the code yourself, it will give the same warning to you if it doesn’t need to be mut
Well, also in C/C++ you can specify a reference is const to be sure it won't be modified, but in this video since they want to prove Rust better they don't tell you that you can be sure a reference doesn't get touched.
@@mattiamarchese6316 C++ doesn't have interior immutability by default. Heck, most programming languages today don't have general immutability by default, it's a very recent shift in programming language design.
@@Speykious for sure, as much as Rust doesn't have general mutability by default. It's just a choice, but if you want to make something immutable in C++ you just have to use const, what's wrong with this?
@@mattiamarchese6316I generally use constant arguments more than mutable arguments. It gets quite annoying to incessantly type "const" everywhere. Not only do I type it several times per hour, but it's also two characters longer than mut. This doesn't sound like much, but I definitely prefer default immutability.
Wow my youtube was autoplaying in the background and then this video came on! Super interesting and loved it! Love these type of coding videos that tell a story/have a goal or objective in them and also show/teach you something about the code! Please keep it up 👍
While yes Rust's default HashMap implementation is faster than C++'s, Rust's default hashing implementation is much slower than C++ (SipHash, which is DoS-resistant, but as a consequence much slower). Is changing the Hasher possible? Yes, infact it is very easy and is directly possible by passing it to the HashMap, but since we're comparing defaults, it's important to remember it's not all green the other side. In fact, slower hashing has often caused my Rust's HashMap to be slower than the C++'s one.
great video, although im confused about the arguments and examples. Regarding the pointer passing and the idea of not knowing if the pointer is supposed to be operated on etc... You could pass that pointer as a const signaling that its read-only, immediately giving the reader an idea of what it is. Also the param names are poorly named too maybe. I think the examples you showed are mostly due to "bad code" practices than C C++ vs rust. About two sum, I would also argue that its not a C C++ limitation either. I personally think rust structure of writing code is a bit confusing, granted it may be due to me being new to it or being used to C.
Great points, thank you for sharing this. Everything you are saying is certainly true. With good practices and smart implementations the differences between C and Rust performance and readability are probably very small. I guess I was really just trying to show that while this is the case, C doesn't try nearly as hard as Rust to lead you towards good practices and smart implementations. That said I totally get what you are saying about Rust structure or syntax being confusing, I felt the same way for the longest time and still think that sometimes. Something about the minimalism of C is so refreshing, even if it can lead to unchecked memory vulnerabilities. Conversely, the Rust website says that, "We do not prize expressiveness, minimalism or elegance above other goals. These are desirable but subordinate goals."
The thing is, the C code would let you write and compile it. The Rust Compiler simply doesn't let you work with data ambiguously. If it can't understand exactly what you're trying to do, you don't get to compile it!
bruh usually if an input is getting modified in c it will be non-const or mutable. and if its not getting modified then it will be const. That divide function segment feels cap as hell, I've been working with c libraries and team projects for a while and with proper commenting and coding standards I never get theses issues of code ambiguity
That's fair. Though I would argue that Rust's implementations are better due to immutability by default rather than C's mutable by default. But definitely you could make it a pointer to a constant value. That segment spawned from my own difficulties writing a simple ext2 file system in C. Lots of library functions took non-const pointers and I never knew what they did with them until searching the web for a bit. But it could easily be due to backwards compatibility and age, and maybe most younger C libraries are more intuitive.
when you explained how in C (and C++) it's more difficult to know what a function does exactly, it is true, sometimes. C programmers learn how to make it more clear. For instance, name the parameter of a function so that it can explain what it does (for instance, errCode), or use the modern intellisense and detail every parameter and return of the function with a comment that will then show up in the IntelliSense. Or even mark a parameter or a function as const. These are all things that most C programmers do. But I get it. Also, in C you could solve the first problem with either marking the parameter as const or making it a refference, then you wouldn't have the change the actual function call, and you don't have to care about it
Is there some type of comment/docblock structure that is recognised by Intellisense for C? I'd love to know. Also, I'd be interested in static code analysers to read these docblocks and assert if they have been respected in the code. I come from the PHP world, where docblock comments are standardised by the PSR-5 standard and Psalm is a static code analyser that gives you a strong type system similar to TypeScript or Rust. I'd love to know if there is something similar for C.
@@aheendwhz1 I can't say exactly for C since I'm a C++ dev, but I know there is something like that. I know that if you put a comment above a function or variable, that comment will be seen in the intellisense. And I think that if you put in @param parameterName and some text, it will display that text as the description of the parameter you specified. And @return should specify what the functions returns
Very true. I intentionally wrote both examples to have basically as much bad practice as possible to try and highlight that the defaults in Rust are forced to be more clear than in C (at least in my opinion, though you could of course argue otherwise). But yeah like you said definitely with good documentation, variable naming, etc the clarity is not really an issue, at least as much as I made it out to be. Thanks for pointing this out. Also I could be wrong but I believe that in C you cannot pass a variable as reference, only in C++. Though again I could be wrong about this. But if that is the case and you can only pass pointers I would also still feel that Rust's implementation of const pointers / references is better since it assumes immutable by default. Anyway thank you again for sharing this
_Safe_ Rust is memory safe. _Unsafe_ Rust is not memory safe, but if you can make sure that what unsafe code you have is sound, then you don't need to worry about any of the safe code.
@@angeldude101 safe rust can leak, so the way the rust standard managed to make safe rust be memory safe is by... simply stating that leaking is considered to be safe despite the fact that it actually is not... damn, so much for safety i guess.
@@AlFredo-sx2yy Memory leaks don't violate type safety, nor do they allow you to access arbitrary memory addresses. They can cause things to break if the program runs for long enough, but it takes enough effort to cause a memory leak (either explicitly calling Box::leak() or using RCs with interior mutability to make a cycle) that it was decided that it wouldn't be too hard to avoid in a long-running service. And then when it does break, it's usually just in the form of crashing, which while very undesirable, especially in the kernel or an embedded system, also isn't memory unsafe. I would like to hear why you claim that memory leaks are as unsafe as type system violations or data races.
@@angeldude101 if you leak on a memory arena / linear allocator of sorts, you're basically going to begin overwriting memory at some point, for example imagine a set of pages / arenas that the programmer has set up to contain a bunch of objects for whatever purpose (game engines and other visualization programs usually reserve space like this), if you now leak enough, you can have your program start writing data on whatever comes after your arena if there's no proper handling, even in safe rust, which means you have basically a buffer overflow. Simplest example i could come up with. Another example: since the heap is usually implemented as a set of memory arenas, if the OS had no protections in place, this would mean that you could potentially start accessing memory from a different program. This is not possible in modenr OSs because of the protection measures in place that prevent this sort of problems, but when you're running code in an embedded system, if you have a small arena for whatever purpose, you could theoretically come across this problem and start overwriting whatever is located in memory after your heap. All of these examples require a bad implementation of a memory arena, obviously... but the problem is that they are all possible to make in SAFE Rust.
I was gonna like the video but then I realized that CodeAhead is using the $5 Bugha microphone, so i retracted my like because my ears fell off, otherwise this was a good video I thoroughly enjoyed it
I do think rust can be faster than C because the borrow checking and the strictness of the language allows the processor to impove branch prediction and reduce overhead due to pointless re-checking and execution duplication of instruction
that's true WARNING: I'm a noob trying to explain complex things, there's a lot of errors and ambiguities in the explanation below. Rust uses RW-Lock pattern (multiple immutable references, single mutable reference) With this, you can assume a &mut will always be pointing to a different piece of data By this knowledge, Rust compiles mutable references equivalently to C++'s (maybe C too?) *restricted ptr These kind of pointers lead to memory optimizations in the binary, since compiler is able to inline values That's why multiple mutable references, even in single-thread, is undefined behavior in Rust
This is incredibly interesting, thanks for sharing. I'd like to maybe do a video on this (after doing lots more research on it). Maybe not but anyway thanks for mentioning this. Quite interesting
@@rayk6562 Runtime reference counting is opt-in, so unless you expertly specify that your data has shared ownership, runtime reference counting will be completely absent from your binary. Compile-time reference counting you could argue is what the Rust compiler does, but it doesn't have a runtime cost.
"Rust is more modern than C and C++ thus is able to have faster default implementations of data structures" -> Oh come on. This has nothing to do with Rust being modern/new. There are currently (if I am not mistaken) 3 implementations of the C++ stl (msvc, gcc, clang) The C++ STL usually describes the data structures in general and doesn't force a specific algorithmic implementation for containers. Moreover, you just tested one data structure. Lastly, one can find very good C++ implementations of any data structures one desires if the STL ones don't fit their needs.
There are no zero cost abstractions in programming. Rust encourages the use of more abstractions than C does. Hence Rust will always be on average slower. (Yes, when fine tuned all systems languages are equally fast with the same compiler backend.)
-O It won't clean up every abstraction, but it will inline most basic method calls, yielding the same assembly as if you wrote the code in hand-optimized C. "Zero cost" means that you couldn't have written faster code manually, so it's more like "zero overhead."
@@angeldude101 "You couldn't have written faster code manually" that does the SAME thing. Maybe... But the abstraction generalises the problem, hence it will never be the most efficient way of solving your particular one. Abstractions force you to think in general terms and code for a wider class of problems. The more you abstract away, the further you are from the optimum. That's what I'm trying to say here.
@@ElPikacupacabra When in doubt, inspect the assembly output. The compiler is smarter than you probably think, especially with optimizations turned on.
@@Selicre The problem is that you won't be implementing the optimal solution, not that you cannot. That's because you over-abstract. For example, std::vector is not the optimal dynamic array implementation in all cases, but you wouldn't know that if you don't see the details relevant to your problem.
As far as I am concerned, you do not have to clone tha string over and over again. You can use &str (string slice) which basically creates a copy, however it deletes itself after the call, thus not overloading the memory, so the clone example is not right, imho.
I like this video, and I like Rust. I'm also a C programmer, and spend most of my time coding in C. That being said, I feel the example of the divide function is quite cherry picked (though I doubt it was intentional cherry picking). C gives us ways to give the caller information about intention through the use of qualifiers like *const*, and *restrict*, and (at least in code I write), it is not uncommon to have an "Error" structure that returns data, as well as an error code wrapped neatly in a structure. This explicitly tells the caller that the function returns an error, and it should be handled. Obviously, one of the main benefits of Rust is that it *forces* you to do all of these things-- it forces you to explain intention, but I feel like that part was left out since you did not mention that C does have ways to communicate intent to the programmer. Or maybe I just missed something in the video, I dunno. EDIT: Clarification.
My understanding is that restrict is very rarely used in actual C code, to the point that it had a long-standing bug in LLVM that was only discovered when Rust started tagging nearly every function with it.
@@angeldude101tbh, in the ehader file for strings (string.h) in gcc, restrict is in nearly every parameter in string functions and can be a useful micro opimization for low level programming, especaly on micro controllers
If you are writing a C program, likely for an embedded system, and you shipped something that will result in a segmentation fault, you failed in your requirements analysis or implementation as an engineer. Ask any embedded engineer with products out in the field, and they will have 10+ years of success with no failures on bootloader or firmware written in C. Want hash maps? Better stick to C++/Java/python/etc where those come easier and with actual maintained and standardized libraries. Us C nerds take pride in our curmudgeonery lol.
In other words, write good code, get good results. Something that people think is impossible in C despite it happening all the time. As far as hash maps, I've found that most standard library implementations are garbage, but the algorithms and data structures library I wrote, and occasionally still add to, has multiple implementations that work perfectly fine and will still work for years into the future.
I can’t help but feel confused about “C < Rust”. If it’s speed we measure less time would be better right 😅. And if it’s meant as the English translation to “greater than” in some sense of general goodness or greatness well then that pun doesn’t work as well for “less than”. You kind of have to read it as “worse than” for the pun to work but that doesn’t really make a good pun with “
So, I might be wrong, but I believe the statement that with every ‘greet(name.clone())’ call you use more memory is technically false. Actually, when you call the greet function, the cloned name value is given to the greet function. Thereby, the greet function becomes the owner of the name clone. Once the greet() function has finished executing and returns to main(), the name clone gets destroyed due to how rust manages memory. Therefore, you use some extraa memory when you clone name. But this code frees this memory before you clone again. You still get a slowdown, because you always copy,but you do not use more memory with more greet calls, as Rust automatically deallocates the name clone once the function greet() returns.
Except, Rust is better at it. I learned more about how things work from reading rust docs to make my rust programs compile, than from debugging my UB-ridden C code. C leaves you with more misconceptions than facts, because it obfuscates stuff while pretending it doesn't.
The speed of Rust depends a lot on what your code actually does. I have a small test benchmark that performs fractal calculations, no fancy objects or structures are involved except arrays, and this code runs 10% faster in Rust than in C, who would have expected that? However, if I modify the benchmark to compute many more fractal values that all need to be held in memory, the Rust code is suddenly 20% slower than the C code. Sure, in the end it's all LLVM, but there are certain operations that are faster in LLVM than others, and certain combination patterns cause more code than others (e.g. because of extra checks or more cleanup at the end of a function). Rust is neither guaranteed faster nor guaranteed slower, it all depends on what your code actually does and how your code does it, and how well the compiler can optimize away the things that would otherwise make Rust slower, or how much the compiler can benefit from making assumptions about data or code flow that it can only make in Rust but not in C, where those assumptions end up leading to better CPU code. Since Rust is much more complicated under the hood than C, the final speed will always be a kind of gamble ("Oh, if I change this, my code will be 60% faster... I didn't expect that"), whereas speed in C is predictable most of the time ("It was clear that this will be faster than that"), but that still doesn't mean that C will always end up with the best performance, as C simply provides less meta-information for compilers to work with, so compilers can't always figure out the best optimization path, as they simply have less understanding of what's really going on, or they have to be prepared to deal with exceptional cases, which may be guaranteed not to happen with comparable Rust code. The takeaway is that for a way safer and way better typed language, Rust is often comparable fast, but in most cases it will still be slightly slower, so you are trading a tiny bit of speed for way more safety. My biggest grief with Rust is that I already disliked C++ just because it's syntax is so ugly but the syntax of Rust is even worse.
There is nothing unsafe about C. People simply don't know how to use it. I also doubt that Rust can beat properly optimized C code if the compiler optimizations are the same. It might not even be the code. Individual results might have something to do with cache hit/miss rates or pipelines rather than the actual code. Even LLVM can not optimize that since it's strongly data dependent.
@@lepidoptera9337C is unsafe because its memory model is unsafe. Unless you use C11 atomics (which didn't even exist prior to 2011), C provides no guarantees about memory access at all. The problem is that every platform/CPU has its own memory rules and you would have to make sure to write C code that follows those rules but since those rules vary by platform/CPU, it's actually not truly possible to write C code that is guaranteed to work correctly on more than one platform. If it does, that's plain luck. Modern programming languages like Rust, Swift, Go and even Java and C# do define a memory model as part of the language that is valid for all platforms the language supports. It's up to the compiler/interpreter to ensure that this memory model works as defined by the language on the platform, no matter what is required to make that happen. The programmer doesn't have to adopt his code to any platform for those, the programmer can rely on what is defined in the language standard. Here's a very simple example: You have a global variable of type int, whose value is currently 0 and you change its value to 10 in thread A. 30 minutes later, the variable has never been touched ever since, you read the value of the variable on thread B, what will the value be? 10? Show me where the C standard makes that guarantee. Fact is: It doesn't. Prior to C11 C didn't even acknowledge the existence of multiple threads. But even without threads: You only have one thread, that runs on CPU 1 and you write a value, 30 minutes later the SAME thread runs on CPU 2 and reads the value. Again, where does the C Standard guarantee that CPU 2 will see the value that CPU 1 has written? It doesn't, because C does not acknowledge the existence of systems with more than one CPU. And what applies to CPUs also applies to cores of a single CPU. Only when using atomic variables as defined by C11, there now is a memory model and you as programmer can even choose between 6 different memory models but for everything that is not atomic, nothing is defined. Without atomics, the way you think C works in fact only works if all your code is always running on a single thread only and your system only has one CPU core or if you are lucky and the CPU does make that guarantee in hardware; like x86 does most of the time but PPC, ARM, Alpha, RISC-V, etc. doesn't make those guarantees. That's why so much C code that worked flawless on x86 CPUs fore decades suddenly breaks when it has to run on other CPUs. The code was never correct in the first place but x86 is very forgiving and gives a lot of memory access and ordering guarantees that other CPUs don't. So you basically had C code, that was valid for x86 in particular but that doesn't make it universally valid, as there is no such thing as universally valid C code, because C leaves most definitions to the system, instead of defining it to the language. This is also true for plenty of other situations. E.g. what happens if a signed int overflows? Not defined in the C standard. What happens when you de-reference NULL? Not defined in the C standard. What happens when you shift a 32 bit integer right by 32? Not defined in the C standard. Of course, on every platform the behavior is defined but the definition varies by platform, that's why modern languages define it as part of the language. As for performance, I have a C code sample, that calculates a Mandelbrot set. It's as basic as this calculation can get (three for loops, an array of float values, some simple int and float calculations, one static function being called from within the main for loop). When I compile it with clang using -O3, the code runs slightly slower as when I take the code as is, convert it to Rust (which almost requires no adoption, except for the data types and the function definitions) and compile it with the Rust compiler. Reproducible on x86 and on ARM CPUs. If UA-cam would allow external links, I'd show you the code and I take any bet, you cannot make the C code run faster than than the Rust code, as there is nothing you could optimize about that code and there is no better optimization than -O3.
@@lepidoptera9337If I could post links here, I could show you a simple Mandelbrot calculation code that will run faster in Rust than in C and I bet that there is nothing you can do to make this code run faster in C, as this code is minimalistic and there is nothing you could optimize that the compiler cannot optimize 100 times better than you and still Rust always wins on x86 and ARM with both compilers set to maximum optimization. And C is unsafe because it does not define a memory model (only for atomics which only exists since 2011) and it also has tons of undefined behavior, where the C standard simply does not define what the outcome of an operation will be. This makes it very easy to port C to any platform but it also means that the same code will not behave the same on different platforms. Modern languages have a well defined memory model and well defined behavior and it is the task of the compiler to ensure this behavior on all supported platforms, no matter what is required to make that happen, so you can rely that the same code will produce the same results on any platform.
@3:32 There is a very simple way to "say" that a function will only reference a pointer: int i_reference(const char *const string); // string is a constant pointer to constant data, aka. immutable int i_mutate(char *const string); // string is a constant pointer to variable data, aka. mutable. int i_am_unsafe_read_doc(char * str) // whoever wrote this, had no regard for safety, purge this from your codebase. All pointers are copied by default in C, because their size is 1 word, so the *const doesn't affect functionality, but shows intent.
C code @3:20 is incorrect. The divide function definition has an integer pointer as first parameter. So when calling the divide function, you pass with & a normal variable not a pointer to the divide function. int a = 20; int return_value = divide(&a, 4); printf("%d ", a);
When's the next video my dude? You've got everything here that makes a great channel, what are you currently passionate about? Tell us about it! 😄 (My only feedback, if I may, would be to use a better mic, don't buy one, just use your phone's microphone like I demo in my "how to sound great" video) Looking forward to seeing more!
Thank you, that means so much coming from the best! I will definitely follow that advice if I end up making another video. I was having a lot of trouble with my adsense account which was killing my motivation, but hopefully the situation can be resolved in the future. Until then I will learn as much as possible to have some material for the future! ;)
Using clone repeatedly as you pass each string to the greet() function isn't using more memory *each* time, because each instance is immediately dropped inside the function. But anyway as others have said only a Rust rookie would write it that way.
@@dylanmashini Certainly. Nobody would say it was a good approach. I'm just noting that what was stated is technically incorrect, and not the reason it's a bad idea. (Edited to fix typo.)
In fact, the algorithm would probably use the same amount regardless due to memory paging. The string should fit in the same page as the original, so it theoretically would just be calling memadvise and not actually use a full allocation. It’s still slower, but isn’t as bad as it could be unless you’re unlucky enough to do this on a page boundary.
Rust is faster than C++ if you don't know how to write good code in C++ Having to check that the location of an array you're accessing isn't out of bounds means bad code (unless the user is involved, the user will find all possible bugs) Even division by zero checks (again, unless the user is involved) means bad code
Excellent video, and something I didn't think of before: newer languages have more recent improvements to algorithms built into the standard library. What I think is a bit implicit though in the topic of 'consider Rust over C/C++ for longevity purposes', is a feature that C and C++ have that also harms it a bit: the guarantee of backwards compatibility with each new language release. This means that bad, old parts of the language still have to be included and maintained, rather than pruning bad or obsolete parts of the language and focusing development on better, newer paradigms. This is not the strategy of C and C++ because it would mean that each language revision would break compatibility with older language versions which would strain development. Newer languages like Rust do not have this problem of having to carry years worth of obsolete features, not least because said features have not (yet) had time to accumulate. This also helps with program development because one needn't sift through a dozen or so options to weed out bad practices that were rendered obsolete by language revisions.
Backwards compatibility is actually something the people behind Rust take seriously and have a beautiful solution: Editions. We could launch a new version of Rust with breaking changes (say like Python 2 to 3) tomorrow - without breaking anything. Every cargo project knows what edition it's in and the compiler will treat them accordingly. Not only can you compile your ancient legacy code with the newest compiler version but you can combine different libraries written to completely different standards and the compiler will figure it out.
Greet takes the String by value and not by reference. Taking by value moves the variable into the function and it doesn't return out again. It is moved out of scope. Clone explicitly creates a new copy, so that it may be moved into the variable.
In Rust, there are 3 main forms that you can pass a variable by. You can pass it by value, in which case you are giving over full control of the variable to the function or new variable. The previous variable name no longer refers to that data. The data has been "moved" into the new variable name/into the function. You can pass it by immutable reference, which lets you look at the data within and do what you want with that data, as long as you promise not to change it. This is super useful as it lets you write functions Finally you can pass it as a mutable reference, which means that you have rhe ability to change the value, but you promise to give it back once you're done using it. An important distinction that isnt usually taught at the same time as this concept is about cloning vs copying. To understand that you need to understand that there are two different parts of the program's menu, the stack and the heap. The stack is super fast, but everything you put on the stack needs to have a size thats known when the program's compiled. Things like numbers, characters, and many enums/structs etc. have a size known at compile time. Other things, like Strings, can be various lengths, so you dont know how much datat they'll need until you know what's going in them. These can't go on the Stack, and instead go on the heap, which is slightly slower. Copying can be done on the stack, and because the size of the thing being copied is already known, the bits of the data can just be copied without any slow checks. Cloning, on the other hand, applies to data on the heap, and involves checking the size of the data, finding a spot to put it, and then copying the data across. Much slower. In some situations, you want to create a second copy of the data, such that the new copy and the original are both accessible. For datat on the heap, clone is well used here. Sometimes though you just want to change some data, or read it for a one off purpose. Passing by reference is better here.
4:20, C++ can return a string empty or with an error message too. But this approach is slow, because it needs to create the string. Faster is to do what C use to do: return an integer, with negative value in case of error. Then, if the user wants to see the error message, it can be fetch in an global array of errors. C can also return a null pointer, in case of success, and a valid pointer to an error message otherwise. But the int8 return is faster. 4:34, C++ can say if a parameter is const or not too. Your example doesn't beat C++ by any means. To be fair, there's 1 case: if they are int pointer and an int, there's a possible performance issue: pointer aliasing. It can be solved by copying the int to a local variable or by using some compiler-specific keyword, such as restrict, but this last solution is not standard yet. This is an annoyance Rust hasn't.
No way you brought up team, intentions and our own intentions and had an image of Young Thug. You sir, have earned a subscriber. Too few of us in this field.
Having the compiler be the sandbox as far as form creation (what memory handling strategy we need for the solution) is great only when you know exactly what the logic of the code is. Good old fashion debugging and sense making (ala Bret Victor interactive infographics) is still necessary for logic checking in the long run.
However, Rust's bigger strength is its type system in general. You can teach the compiler all sorts of new invariants to enforce. For example, the typestate pattern lets you teach it to check for correct traversal of state machines at compile time. (The Hyper HTTP library uses it so that things like "tried to set a header after the request body began streaming" are compile-time errors.) With C++, you need inefficient and verbose runtime checks to match that because, without a borrow checker, there's no compile-time way to prevent code from holding and using stale references if you implement the typestate pattern.
@@ssokolow I don't really like do defend C++, but I would here for the sake of the reason and truth. In C++ you probably can express typestates (I have seen somes in Github using classes and templates), but in general I would say implementing them in Rust is way easier and safer because of the strictness of the language compared to C++.
@@diadetediotedio6918 That's more or less what I intended to be saying. I've seen typestate implementations in C++ too, but the authors always make it clear that, without a borrow checker, it's possible to hold onto and use stale states or that they have the performance overhead of runtime safety checks.
I'm relatively new to Rust but I'd say in this example there is no "real" difference between C or Rust in terms of writing the implementation. The Rust code was easily readable to me, even as a beginner, because I am familiar with the syntax (I was screaming "just pass a reference" like 20 times) because thats what I am used to. And tbh the C code seemed to do the same (char *). The difference likely lies within a lack of dedicated Rust libraries C/ C++ had for decades and being forced to handle Results / Options, Err etc. whereas C will let you do whatever. This will be a negative for short POCs but in large code bases you will implement the exact same things that are standard in Rust anyway
hmm, this was very interesting. I do really like that it would force you to explicitly mark mutability. I may consider writing my future code in Rust...
rust without llvm and gcc doesn't even exists and this people are morons XD all their code works with or thru c++ yet they don't like c++ and c cuz they are just bunch of looser typescript developers who never heard of memory managinG XD their brain cannot just process the new info about compuer science : D
Your explanation is great. Here is my doubt, I have written a program that has 4 threads between every thread busy waiting on the segment queue. We are creating a packet in one thread and passing via queue to another and processing something in every thread and that packet processed in the last thread and we time stamp at every thread. Total 1million packet processed. We have written a similar program in c++ also. Performance : MACOS: rust : 2.9sec c++ : 2.7sec LINUX rust : 15sec C++ : 3.3sec Why rust is too slow in Linux. Can anybody help me to solve my problem, I have used every compiler optimization of everything, still nothing happens.
Your first example, with the divide function, is not very well thought out/fair. You can make your intentions about parameters very clear in C/C++ by using const pointers or const references. And personally, I like mutable by default. As for your hashmap example, have you compared every commonly used container that are similar in purpose in both languages (static arrays, vectors, sets, ...)? Otherwise it's just cherry picking on the level of "Look, everyone, I found the one container where the standard implementation of Rust is faster, clearly that means it is better overall!!!".
I don't think there is any language compariable to C in my field, that is, directly manipulating hardware, like peripherals and registers. Not even C++. However in fields at a little higher level than that, C/C++ is definitely not the first thing I would consider. Normally I would put all hardware I/O operations into a dll written with C, and use Python or Rust to handle higher level operations.
Outside of the code writing I believe that Rust is great for teamwork because of the config file, the toml I think. Just type in whatever dependencies you need in there and cargo will do it all for you, all the correct pakages, the correct version of Rust you want and then cargo run, kinda like Python venv. So not only does Rust code is explicit and precise, but the way Rust manages itself is just so good, any team member with Rust installed can just take that toml file and run it, and they are set.
I dislike your example which shows Rust forcing display of intent with its syntax. It's possible to pass via const ref in Cpp too to make it clear if we are modifying the args or not
U can't just say "when we benchmark" show an actual benchmark and compiler details... I would bet c usually wins if u choose a good hashmap especially if u optimize for ur data distribution
The only thing I can like Rust to is Bloodborn: frustrating as hell to initially pick up, but once you understand that it’s actively teaching you how to use it just by engaging, it’s so gratifying
Even tho I agree that rust is better at documenting and validating intentions, I feel the "rewrite in rust is easier" logic was fallacious. That example proves nothing about rust, even if it proves that that particular software in C was a mess.
@@JorgetePanete c and c++ ain't for kids to play with it XD go and type in rust crap who doesn't even exist and who cannot evne exist without c/c++ cuz guess what ? it's built upon gcc and/or llvm both are written inc./c++ XD so keep crying kid and go and write your crap language which calls itself RUST XD lolz
C does have return types. However there is not one standard way you handle errors. Unlike Rust resut or option. One function could set errorno, another could pass back a null ptr, one could send back data you have to pass to an error checking function. This causes a lot of context switching and searching for the correct way to handle every error. This leads to bugs, slow coding Or lack of error handling. While C is great In a lot of ways it has trouble allowing/forcing uses to handle errors properly.
I think we should be speaking more about the advantages and disadvantages of each programming language, and thus concluding which is better suited for which situations and why. Yes, C is old, not as friendly as Rust, and can be a headache at times, but it fulfills excellent its original purpose: writing operating systems. As with all technologies, we should know which one to use and when.😄
Yeah I apologize for that. I knew publishing the video that the audio was quite bad but I never expected the video to get this much attention, so I didn't think it mattered really. I will have to invest in some better equipment I think.
Ehhh the reason why `std::unordered_map` is slower than Abseil's flat hash map is because it makes certain guarantees that the Abseil hash map doesn't make. There is no one-size fits all solution for containers unfortunately, only trade-offs. If you have really strict performance demands, you're going to have to find an implementation that makes the right trade-offs for your application.
At least someone who really understand... this video is quite pointeless
@@Erryjet ikr... this video was basically comparing the result of using completely different implementations of a hashmap, and not the difference between the languages. I also love how at first he said that the C program was easier to write because you had to tell the Rust compiler exactly what you wanted, and then at the end he says that all of the examples he's show somehow prove the rapidity of development in rust and i cant help but be completely baffled at the pointlessness of this video.
I would say, std::unordered_map optimized for very-very rare scenario. Open addressed hashmaps are better suited for the main purpose of hash map: associative arrays.
too smart note for video about rust
A professor of mine also remarked in a recent C++ lecture how he was wrong in the past for using vectors for everything, which would cause hiccups in certain scenarios. Learning the differences and usecases for all containers is tedious, but rewarding.
The first example is great to illustrate in what way Rust forces you to get things right, but in reality this is something only a beginner would likely have a problem with. Once you get used to enough Rust (use it for two weeks or something), you pick up certain patterns, such as using &str (string slice) anywhere you need to use a string, unless in specific cases where you need to own one, for instance when initializing a struct. And in fact, using &str instead of String in this case makes it _actually_ equivalent to the C version where a char* was used (almost, since Rust works with utf8-checked string slices which have a length instead of zero-terminated strings, but that's just a detail).
Yeah great point, another commenter said something similar and I agree. Definitely most prevalent among the beginner / average programmer, or even an experienced programmer who is just a beginner at Rust. Also I did not know that about Rust string slices vs. C char pointers. Thank you for sharing!
@@CodeAhead You're welcome! :)
Well, that detail, having UTF-8 by default in a programming language, is a really great one. And it's one of those things in which the language does not only do a greater job than C and C++, but also compared to some higher level languages like Java and C#, where UTF-8 isn't the default either (those languages use Unicode UTF-16 by default in memory, UTF-8 is only the default for things like file IO operations).
@@jongeduard well most Windows and Mac OS X APIs use UTF-16 unicode internally, possibly forcing a conversion from UTF-8 to UTF-16 when calling those with these strings, that's also why QT choiced QString's implementation to use UTF-16 strings, but yeah it would be cool if they had native UTF-8 support , i saw some people talking about windows having new UTF-8 support but from what i searched it seems like that supports is mostly converting your UTF-8 string to UTF-16 which is still slower
utf8 is the encoding of the internet and the future though
"Rust is faster, because I'm using a third party component that is faster"
Don't get me wrong, I get your point and the first example is pretty good, but the second one doesn't prove any point. C++'s implementation of the STL (or other stdlibs) is at most *suggested* by the standard committee, never implemented. The compiler / environment developers can choose what algorithms to actually use to implement them. I'm certain there is C++ libs that use Abseil's hashmap implementation for std::unordered_map. Also, C++ is not dying nor obsolete, since very useful revisions and redesigns are happening (c++17, c++20, c++23, ...)
There are basically two points that you seem to be missing, both relating to composability:
1. Rust's strong encapsulation guarantees at the API level and unwillingness to rush into having a stable ABI mean that the Rust standard library can and has already had its stuff replaced with faster implementations, while C++ is stuck with slow implementations of various things that can't be upgraded without ABI breaks. (See "The Day The Standard Library Died" by cor3ntin, for example.)
2. As Bryan Cantrill says in the segment at 40:53 in "Is It Time to Rewrite the Operating System in Rust?", those guarantees make people more willing to engage in more aggressive optimizations.
He's talking in the context of C, but C++'s decisions to remain so close to C for FFI compatibility, and the resultant memory unsafety, mean that it has similar problems. (eg. I've heard various examples of developers adopting "when in doubt, make a copy" policies around std::string and std::string_view in C++ for lack of the guarantees Rust's borrow checker provides surrounding string slices.)
To quote the most important part of that Bryan Cantill bit I mentioned:
"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."
Rust isn't just about memory safety, it's about a pervasive effort to ensure that you only need to reason locally about code's behaviour.
@@ssokolow Thanks for taking the time to elaborate. That's a point I can get behind. Wish it was made in the video
@@BGFutureBG Chandler Carruth has a number of talks at CppCon on UA-cam that discuss the problems with the stdlib in C++. The APIs themselves require certain implementation details that are incompatible with the optimizations seen in the abseil flat hash map and Rust. A vendor cannot use this in their C++ standard library while also being compliant.
That said, abseil is public, so it's not like you can't get the thing in C++. It's just a shame that the default option in C++ is often not very good. I'm a C++ Dev that has never used Rust, just annoyed by the language being unexpectedly slow for even simple tasks at times.
I'm pretty sure C++ standard doesn't specify what algorithm should be used for a hashmap and afaik the implementation in GNU libstdc++ is faster than the one in Rust
EDIT: I just tested it with 5000000 insertions, this is the time:
C++ (g++): 309 ms
C++ (clang): 249.406 milliseconds
Rust (rustc): 394 milliseconds
It's actually first video about Rust I watched that actually convinced me that spending my time fighting the compiler could be worth it.
You'll come to realise that it no longer becomes fighting the compiler, it truly starts becoming a like a peer programmer helping you out always making sure you write safe code
You have the choice to fight with the compiler or with the code docs, server errors and clients.
Do you want to fight against one big army now or 1000s of small ambushes in the next 5 years?
@@wumwum42 Mostly this. You will learn to write compiling code faster on the first try or in not that much time. Languages like C will still compile if the program is incorrect and interpreted languages don't have that check at all and you **will** get unforeseen runtime errors in production. Unit tests can only catch these bugs if you make one that tests for that bug for each function. rustc does this before it compiles your tests. It's so much faster in that sense, even python can't hold a candle to this.
How fast can you make that application? For python that may be faster than rust in general, by maybe a factor 2. How fast can you make it correctly? Rust will certainly win this one with a far bigger margin.
It's a silly thing to do considering the Rust compiler always wins the fight.
@@jdiehl2236 plot twist, I still don't code in Rust and don't plan to.
In the first example, the Rust code is equivalent line by line to the C code, the only reason you'd have issues writing is if you simply don't know the language. The same would then be applicable to C: if you've never used printf before, it's likely going to take you some time to figure it out.
In the divide example, again: The rust code is basically equivalent to the C code, because in C you would mark the pointer as const if the function doesn't need to write to it. It's a convention that I have always seen used everywhere, so it isn't really that ambiguous. As for the return type, if you really wanted to make that clear, a simple typedef would allow you to change the int into something more explicit.
As for the std::unordered_map, you can provide your own hash class to make it faster. I have seen benchmarks in which C++ absolutely crushes Rust, depending on the provided hash function. That said, the choice of said hash function is still up to the programmer, so the code will be a bit harder to write.
Generally speaking I would say C does indeed require more time to write an equivalent amount of code, simply because you have to do a lot of work yourself. As for C++, I'm not convinced this is the case.
By the way, C++ is faaaaaar from being in the same state as Cobol, it is still an actively developed langage, and is to this day still very widely used in the software industry, unlike Cobol which you will only encounter in very specific codebases, purely for legacy reasons.
Take a look at job offerings for C++, Rust, and Cobol, I'm pretty certain the first one will give you way more results than the other two.
Thanks god, someone pin this guy
UA-cam recommended this video to me again, and watching it a second time just shows more of the flaws in logic: Twitter developers re-writing something in Rust faster than refactoring C code doesn't tell us anything about whether the C code was good, readable, or idiomatic - It could, but it is more likely just telling us that these particular devs didn't understand it as well as they understood Rust. Then the video switches to "well the default hashmap library in rust is faster" without discussing why that is so - hashing algorithms can be as specific or generic as you can make them, and it is all about tradeoffs - maybe "cody's random hashmap" library is actually better for a particular use case than the standard Rust hashmap, but there is a performance cost that is worth it in the long run - the only way you'd know is reading the code or the documentation to find out - which is the same thing regardless of whether you use Rust or C for your implementation.
Both examples of when coding can be hard seem to just be skill issues. In the Rust example, one would only write that code if they didn’t know how Rust memory management works. In the C example, one would only use those hash implementations if they didn’t know how their libraries worked. In either case there’s a steep learning curve; Rust is hard to learn and get used to, and a bad library is hard to understand. Difficulty to develop something is usually a function of developer skill and understanding of the codebase.
Although on the subject of libraries, that comparison can fall apart depending on the libraries, and one would be ill advised to assume that Rust’s standard library will always be faster than an external library.
In general, I don’t think claiming that Rust is faster than C is a great strategy. At the language level, C is still faster, and any algorithm that can be implemented in Rust can be implemented in C. Performance is part of Rust’s appeal, but its main selling point is memory management. Rust is a bit slower than C, but it’s still faster than most languages and in return you have guaranteed memory safety.
Interesting, that is very true. Thank you for sharing this, these are great points. To your point I would say that although you could do anything in C that you could in Rust, if memory safety is an issue it would probably be much faster (depending on the content and size of the project) to do it in Rust. If not an issue, definitely C would be the easier and faster (both in performance and productivity) choice. I should have been more clear about this in the video. Thanks again
I agree with your comments. This video felt more "pro-Rust" to me than an actual comparison. I mean look at the title of the video. Your point about skills is spot on. I'm very interested in learning more about Rust and I think it's fantastic, but I also think it's critical to keep comparisons like this as objective as possible to avoid sounding "fanboy-ish".
Rust is a bit slower than C *on the benchmarks game*. Real-world situations keep showing Rust implementations to be faster because teams are willing to engage in more aggressive optimizations and are able to reuse more code written by skilled third parties.
Listen to the segment of Bryan Cantrill's "Is It Time to Rewrite the Operating System in Rust?" at 40:53. The key part is this:
"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."
@@ssokolow You make some great points, but they really point out @mattpattok3837's point. Those are "skills" issues. I would argue in favor of Rust because there just aren't as many people as "skilled" in C as there are in other languages, these days. "gnarly" programming can be a desirable challenge to some and something to completely avoid to others. Hence the interest in using third party libraries for so many things, these days. With that being stated, I certainly do understand the productivity arguments. One could argue programming in Rust can be far more productive because you're not spending as much time focusing on as many programming details, as you would have to if programming in C.
@@TheCocoaDaddy Skill does play a part but empirical evidence seems to show that it becomes exponentially more difficult for humans working in groups to write correct C or C++ as group and project size increase. Personally, I'd see it less as "not enough skilled coders" and more as "not enough reckless coders".
A compelling video but C still reigns supreme for me concerning bytecode interpreters.
Conversations with Rust users:
Newbie - "God this code takes ages to write feels unnecessarily verbose"
Professional - "ACTUALLY THAT'S A GOOD THING YOU'RE JUST A BAD PROGRAMMER RUST IS FLAWLESS"
Conversations with C/C++ users:
Newbie - "God this fucking language is horrible and I'm in immense pain"
Professional - "yep"
Just to clarify, I genuinely think Rust is probably a better language than C/C++ and I'm all for its widespread adoption, but holy shit if I don't find people constantly singing its praises annoying
You don't say. The evangelism is real with this language. I wish we could remove all the prejudices and the attachments and the emotion and just argue honestly whether something is "always" better or whether there are some trade-offs we need to consider in all these circumstances. As Linus said, religious overtones. We ought to move to programming atheism instead.
3:40 In bigger coding projects you should always specify function parameters as they are intended to be used. For example making param1 in your div() function a pointer to a constant value.
int div(const int *param1, ...) if it is read-only
I think you gave an unfair example for c/c++. Though yes variables are mutable by default the const keyword would have fixed the problem of not knowing if the input is modified or not. I believe that it’s just easier to write bad c/c++
Agree that const solves many things in C++. Still, there are problems with lifetimes that the compiler doesn't even warn about that can give anwhere from wrong result to segfaults / other memory errors. I hear that Rust has solved that sort of thing. I need to learn about it.
This will be the case in 100% of these "Rust is faster than C" videos. The purpose of them is clickbait to supply for mental-masturbation for Rust enthusiasts. I like Rust a lot, but it is important to stay grounded in reality, and not let one's preference of language be such a bias and defy logic.
I'm not sure I dig this argument of Rust having better defaults as an explanation to why it is faster. In this specific case it is, but in others it can be different. I remember being unable to match some very simple Go program's performance and having to dig into Google's baked-in implementations to see what they were doing differently from Rust's std library. And at the end of the day I didn't manage to do it still. Sometimes it will be faster, sometimes it won't.
However, the one advantage that Rust has regarding speed is zero-cost abstractions, and especially its very well optimized iterators which compile down to for loops and if statements.
@@jh29a I'm one of his patrons lol, but it wasn't copied, I just remember he said that and find that it is the best way to explain why iterators are fast xd
Wow, great video. I'm stunned it's the first video on your channel. Very informative and very well presented. That's it, tomorrow I'm starting a project to learn Rust (this time I mean it :D) Subscribed, obviously.
Thank you so much!!!! Glad you enjoyed :)
How did the project go?
Yeah there's clearly slot of inspiration from No Boilerplate's and Fireship's UA-cam channel (which is a good thing BTW)
No Boilerplate - youtube.com/@NoBoilerplate
Fireship - youtube.com/@Fireship
I don't know why UA-cam keeps recommending me Rust videos when I prefer C/C++ more. Guess I'll have to manually start telling UA-cam to not recommend Rust to me.
Maybe this is an oportunity for you to learn Rust now
@@diadetediotedio6918 Tried it before and definitely prefer C/C++ over it.
@@_M_643
Interesting, why that's the case? I'm trully curious
@DiadeTedio Tedio Well, I decided to check it out, knowing that it has a bit of a cult fanbase, which initially drove me off.
It was a painful experience because the compiler wants you to do things in a specific way, and anything different than that is an error. Basically, it doesn't allow me to do things that C or C++ would normally allow. I don't care about memory safety if I can't do things my own way.
@@_M_643
I would say C++ is much more "cultish" than Rust by a larger ammount, but this is just myself talking, because I'm in contact with the C++ community longer than I'm in contact with the Rust community, but ok.
For the next, have you tried Go? It is safer than C or C++, relatively fast and has some of this "freedom" to do different things, maybe it would be good for you to learn hard some other languages, just for fun and because learning is almost all good. Maybe you just don't like Rust, but if you recognize the need of the language when safety a concern, that's fine!
4:40 The compiler gets angry with you if a variable is marked mut but isn’t changed, so unless the person writing the code is being purposefully deceptive, you can be sure that it will be changed. And when you try to compile the code yourself, it will give the same warning to you if it doesn’t need to be mut
The compiler will get angry if no codepath changes the variable. It still might not change, it might not even be a common path
Well, also in C/C++ you can specify a reference is const to be sure it won't be modified, but in this video since they want to prove Rust better they don't tell you that you can be sure a reference doesn't get touched.
@@mattiamarchese6316 C++ doesn't have interior immutability by default. Heck, most programming languages today don't have general immutability by default, it's a very recent shift in programming language design.
@@Speykious for sure, as much as Rust doesn't have general mutability by default.
It's just a choice, but if you want to make something immutable in C++ you just have to use const, what's wrong with this?
@@mattiamarchese6316I generally use constant arguments more than mutable arguments. It gets quite annoying to incessantly type "const" everywhere. Not only do I type it several times per hour, but it's also two characters longer than mut. This doesn't sound like much, but I definitely prefer default immutability.
Wow my youtube was autoplaying in the background and then this video came on! Super interesting and loved it! Love these type of coding videos that tell a story/have a goal or objective in them and also show/teach you something about the code! Please keep it up 👍
Thank you!!
Fantastic video! Got here from UA-cam recommendations. :)
Thanks, glad you enjoyed!
While yes Rust's default HashMap implementation is faster than C++'s, Rust's default hashing implementation is much slower than C++ (SipHash, which is DoS-resistant, but as a consequence much slower). Is changing the Hasher possible? Yes, infact it is very easy and is directly possible by passing it to the HashMap, but since we're comparing defaults, it's important to remember it's not all green the other side. In fact, slower hashing has often caused my Rust's HashMap to be slower than the C++'s one.
great video, although im confused about the arguments and examples. Regarding the pointer passing and the idea of not knowing if the pointer is supposed to be operated on etc... You could pass that pointer as a const signaling that its read-only, immediately giving the reader an idea of what it is. Also the param names are poorly named too maybe. I think the examples you showed are mostly due to "bad code" practices than C C++ vs rust. About two sum, I would also argue that its not a C C++ limitation either. I personally think rust structure of writing code is a bit confusing, granted it may be due to me being new to it or being used to C.
Great points, thank you for sharing this. Everything you are saying is certainly true. With good practices and smart implementations the differences between C and Rust performance and readability are probably very small. I guess I was really just trying to show that while this is the case, C doesn't try nearly as hard as Rust to lead you towards good practices and smart implementations. That said I totally get what you are saying about Rust structure or syntax being confusing, I felt the same way for the longest time and still think that sometimes. Something about the minimalism of C is so refreshing, even if it can lead to unchecked memory vulnerabilities. Conversely, the Rust website says that, "We do not prize expressiveness, minimalism or elegance above other goals. These are desirable but subordinate goals."
The thing is, the C code would let you write and compile it. The Rust Compiler simply doesn't let you work with data ambiguously. If it can't understand exactly what you're trying to do, you don't get to compile it!
First video, and it's already a banger! Good pacing, clear voice, good supporting visuals. Nice!
Thanks so much!!
Great video. And the young thug picture you kept using made me laugh so hard for some reason
06:04 "compiling with g++" you should have used "clang++" not "g++" since by default it uses LLVM like rust, this is not the case for gcc/g++
Interesting, I will keep that in mind going forward. Thank you!!
bruh usually if an input is getting modified in c it will be non-const or mutable. and if its not getting modified then it will be const. That divide function segment feels cap as hell, I've been working with c libraries and team projects for a while and with proper commenting and coding standards I never get theses issues of code ambiguity
That's fair. Though I would argue that Rust's implementations are better due to immutability by default rather than C's mutable by default. But definitely you could make it a pointer to a constant value. That segment spawned from my own difficulties writing a simple ext2 file system in C. Lots of library functions took non-const pointers and I never knew what they did with them until searching the web for a bit. But it could easily be due to backwards compatibility and age, and maybe most younger C libraries are more intuitive.
when you explained how in C (and C++) it's more difficult to know what a function does exactly, it is true, sometimes. C programmers learn how to make it more clear. For instance, name the parameter of a function so that it can explain what it does (for instance, errCode), or use the modern intellisense and detail every parameter and return of the function with a comment that will then show up in the IntelliSense. Or even mark a parameter or a function as const. These are all things that most C programmers do. But I get it.
Also, in C you could solve the first problem with either marking the parameter as const or making it a refference, then you wouldn't have the change the actual function call, and you don't have to care about it
Is there some type of comment/docblock structure that is recognised by Intellisense for C? I'd love to know. Also, I'd be interested in static code analysers to read these docblocks and assert if they have been respected in the code.
I come from the PHP world, where docblock comments are standardised by the PSR-5 standard and Psalm is a static code analyser that gives you a strong type system similar to TypeScript or Rust. I'd love to know if there is something similar for C.
@@aheendwhz1 I can't say exactly for C since I'm a C++ dev, but I know there is something like that. I know that if you put a comment above a function or variable, that comment will be seen in the intellisense. And I think that if you put in @param parameterName and some text, it will display that text as the description of the parameter you specified. And @return should specify what the functions returns
Very true. I intentionally wrote both examples to have basically as much bad practice as possible to try and highlight that the defaults in Rust are forced to be more clear than in C (at least in my opinion, though you could of course argue otherwise). But yeah like you said definitely with good documentation, variable naming, etc the clarity is not really an issue, at least as much as I made it out to be. Thanks for pointing this out. Also I could be wrong but I believe that in C you cannot pass a variable as reference, only in C++. Though again I could be wrong about this. But if that is the case and you can only pass pointers I would also still feel that Rust's implementation of const pointers / references is better since it assumes immutable by default. Anyway thank you again for sharing this
@@aheendwhz1en.m.wikipedia.org/wiki/Doxygen might be the one you're looking for.
@@CodeAhead You are not wrong. There are no internal pointers in C like there are in CPP
Such an excellent summary of what rust brings to the table. Whenever people ask me "Why rust?" I'll send them here.
Very nice. Last weekend I started learning some rust - and now I've been inspired to get back to it again this weekend!
Rust is completely memory safe? Challenge accepted
_Safe_ Rust is memory safe. _Unsafe_ Rust is not memory safe, but if you can make sure that what unsafe code you have is sound, then you don't need to worry about any of the safe code.
@@angeldude101 safe rust can leak, so the way the rust standard managed to make safe rust be memory safe is by... simply stating that leaking is considered to be safe despite the fact that it actually is not... damn, so much for safety i guess.
@@AlFredo-sx2yy Memory leaks don't violate type safety, nor do they allow you to access arbitrary memory addresses. They can cause things to break if the program runs for long enough, but it takes enough effort to cause a memory leak (either explicitly calling Box::leak() or using RCs with interior mutability to make a cycle) that it was decided that it wouldn't be too hard to avoid in a long-running service. And then when it does break, it's usually just in the form of crashing, which while very undesirable, especially in the kernel or an embedded system, also isn't memory unsafe.
I would like to hear why you claim that memory leaks are as unsafe as type system violations or data races.
@@angeldude101 if you leak on a memory arena / linear allocator of sorts, you're basically going to begin overwriting memory at some point, for example imagine a set of pages / arenas that the programmer has set up to contain a bunch of objects for whatever purpose (game engines and other visualization programs usually reserve space like this), if you now leak enough, you can have your program start writing data on whatever comes after your arena if there's no proper handling, even in safe rust, which means you have basically a buffer overflow. Simplest example i could come up with.
Another example: since the heap is usually implemented as a set of memory arenas, if the OS had no protections in place, this would mean that you could potentially start accessing memory from a different program. This is not possible in modenr OSs because of the protection measures in place that prevent this sort of problems, but when you're running code in an embedded system, if you have a small arena for whatever purpose, you could theoretically come across this problem and start overwriting whatever is located in memory after your heap.
All of these examples require a bad implementation of a memory arena, obviously... but the problem is that they are all possible to make in SAFE Rust.
I was gonna like the video but then I realized that CodeAhead is using the $5 Bugha microphone, so i retracted my like because my ears fell off, otherwise this was a good video I thoroughly enjoyed it
Nice video brother, keep it up!
First upload and that too good quality? Fabulous.
I do think rust can be faster than C because the borrow checking and the strictness of the language allows the processor to impove branch prediction and reduce overhead due to pointless re-checking and execution duplication of instruction
i was literally thinking the same. disappointed that no one else pointed this out.
that's true
WARNING: I'm a noob trying to explain complex things, there's a lot of errors and ambiguities in the explanation below.
Rust uses RW-Lock pattern (multiple immutable references, single mutable reference)
With this, you can assume a &mut will always be pointing to a different piece of data
By this knowledge, Rust compiles mutable references equivalently to C++'s (maybe C too?) *restricted ptr
These kind of pointers lead to memory optimizations in the binary, since compiler is able to inline values
That's why multiple mutable references, even in single-thread, is undefined behavior in Rust
This is incredibly interesting, thanks for sharing. I'd like to maybe do a video on this (after doing lots more research on it). Maybe not but anyway thanks for mentioning this. Quite interesting
Don’t you lose any advantage you gained because of the reference counting and branching needed to skip freeing the result?
@@rayk6562 Runtime reference counting is opt-in, so unless you expertly specify that your data has shared ownership, runtime reference counting will be completely absent from your binary.
Compile-time reference counting you could argue is what the Rust compiler does, but it doesn't have a runtime cost.
"Rust is more modern than C and C++ thus is able to have faster default implementations of data structures" -> Oh come on. This has nothing to do with Rust being modern/new. There are currently (if I am not mistaken) 3 implementations of the C++ stl (msvc, gcc, clang) The C++ STL usually describes the data structures in general and doesn't force a specific algorithmic implementation for containers. Moreover, you just tested one data structure. Lastly, one can find very good C++ implementations of any data structures one desires if the STL ones don't fit their needs.
Thanks for telling me about swiss tables. I added it to my C++ library bookmarks folder ;D
Of course! Glad to hear that
Great, I was just about the rewrite the facebook backend and found this!
I don't let the compiler tell me what to do, I'm an American!
among all videos about rust and why is it that beautiful i find this one of the best ones!
(sorry for bad english)
Thank you!!
Very informative!
There are no zero cost abstractions in programming. Rust encourages the use of more abstractions than C does. Hence Rust will always be on average slower. (Yes, when fine tuned all systems languages are equally fast with the same compiler backend.)
-O
It won't clean up every abstraction, but it will inline most basic method calls, yielding the same assembly as if you wrote the code in hand-optimized C. "Zero cost" means that you couldn't have written faster code manually, so it's more like "zero overhead."
@@angeldude101 "You couldn't have written faster code manually" that does the SAME thing. Maybe... But the abstraction generalises the problem, hence it will never be the most efficient way of solving your particular one. Abstractions force you to think in general terms and code for a wider class of problems. The more you abstract away, the further you are from the optimum. That's what I'm trying to say here.
@@ElPikacupacabra When in doubt, inspect the assembly output. The compiler is smarter than you probably think, especially with optimizations turned on.
If it compiles to the same thing, it's a zero-cost abstraction, so yes, they do exist. I don't see your point.
@@Selicre The problem is that you won't be implementing the optimal solution, not that you cannot. That's because you over-abstract. For example, std::vector is not the optimal dynamic array implementation in all cases, but you wouldn't know that if you don't see the details relevant to your problem.
As far as I am concerned, you do not have to clone tha string over and over again. You can use &str (string slice) which basically creates a copy, however it deletes itself after the call, thus not overloading the memory, so the clone example is not right, imho.
Good video so I am subscribing
I like this video, and I like Rust. I'm also a C programmer, and spend most of my time coding in C. That being said, I feel the example of the divide function is quite cherry picked (though I doubt it was intentional cherry picking). C gives us ways to give the caller information about intention through the use of qualifiers like *const*, and *restrict*, and (at least in code I write), it is not uncommon to have an "Error" structure that returns data, as well as an error code wrapped neatly in a structure. This explicitly tells the caller that the function returns an error, and it should be handled.
Obviously, one of the main benefits of Rust is that it *forces* you to do all of these things-- it forces you to explain intention, but I feel like that part was left out since you did not mention that C does have ways to communicate intent to the programmer. Or maybe I just missed something in the video, I dunno.
EDIT: Clarification.
My understanding is that restrict is very rarely used in actual C code, to the point that it had a long-standing bug in LLVM that was only discovered when Rust started tagging nearly every function with it.
@@angeldude101tbh, in the ehader file for strings (string.h) in gcc, restrict is in nearly every parameter in string functions and can be a useful micro opimization for low level programming, especaly on micro controllers
If you are writing a C program, likely for an embedded system, and you shipped something that will result in a segmentation fault, you failed in your requirements analysis or implementation as an engineer. Ask any embedded engineer with products out in the field, and they will have 10+ years of success with no failures on bootloader or firmware written in C.
Want hash maps? Better stick to C++/Java/python/etc where those come easier and with actual maintained and standardized libraries.
Us C nerds take pride in our curmudgeonery lol.
In other words, write good code, get good results. Something that people think is impossible in C despite it happening all the time. As far as hash maps, I've found that most standard library implementations are garbage, but the algorithms and data structures library I wrote, and occasionally still add to, has multiple implementations that work perfectly fine and will still work for years into the future.
I can’t help but feel confused about “C < Rust”. If it’s speed we measure less time would be better right 😅. And if it’s meant as the English translation to “greater than” in some sense of general goodness or greatness well then that pun doesn’t work as well for “less than”. You kind of have to read it as “worse than” for the pun to work but that doesn’t really make a good pun with “
So, I might be wrong, but I believe the statement that with every ‘greet(name.clone())’ call you use more memory is technically false.
Actually, when you call the greet function, the cloned name value is given to the greet function. Thereby, the greet function becomes the owner of the name clone. Once the greet() function has finished executing and returns to main(), the name clone gets destroyed due to how rust manages memory. Therefore, you use some extraa memory when you clone name. But this code frees this memory before you clone again. You still get a slowdown, because you always copy,but you do not use more memory with more greet calls, as Rust automatically deallocates the name clone once the function greet() returns.
You are correct, this was a mistake I made in the video. I apologize for that, and thank you for pointing this out!
@@CodeAhead thx for reading comments and interacting with your usebase. And no need to apologize for that. The essential message is still correct.
C is good for learning *how* things work.
Rust simply makes work good. 🦀
Except, Rust is better at it. I learned more about how things work from reading rust docs to make my rust programs compile, than from debugging my UB-ridden C code. C leaves you with more misconceptions than facts, because it obfuscates stuff while pretending it doesn't.
@@KohuGaly dude... It was light humor...
Great video, subbed
Nice video, looking forward to others like this
The speed of Rust depends a lot on what your code actually does. I have a small test benchmark that performs fractal calculations, no fancy objects or structures are involved except arrays, and this code runs 10% faster in Rust than in C, who would have expected that? However, if I modify the benchmark to compute many more fractal values that all need to be held in memory, the Rust code is suddenly 20% slower than the C code. Sure, in the end it's all LLVM, but there are certain operations that are faster in LLVM than others, and certain combination patterns cause more code than others (e.g. because of extra checks or more cleanup at the end of a function). Rust is neither guaranteed faster nor guaranteed slower, it all depends on what your code actually does and how your code does it, and how well the compiler can optimize away the things that would otherwise make Rust slower, or how much the compiler can benefit from making assumptions about data or code flow that it can only make in Rust but not in C, where those assumptions end up leading to better CPU code. Since Rust is much more complicated under the hood than C, the final speed will always be a kind of gamble ("Oh, if I change this, my code will be 60% faster... I didn't expect that"), whereas speed in C is predictable most of the time ("It was clear that this will be faster than that"), but that still doesn't mean that C will always end up with the best performance, as C simply provides less meta-information for compilers to work with, so compilers can't always figure out the best optimization path, as they simply have less understanding of what's really going on, or they have to be prepared to deal with exceptional cases, which may be guaranteed not to happen with comparable Rust code. The takeaway is that for a way safer and way better typed language, Rust is often comparable fast, but in most cases it will still be slightly slower, so you are trading a tiny bit of speed for way more safety. My biggest grief with Rust is that I already disliked C++ just because it's syntax is so ugly but the syntax of Rust is even worse.
There is nothing unsafe about C. People simply don't know how to use it. I also doubt that Rust can beat properly optimized C code if the compiler optimizations are the same. It might not even be the code. Individual results might have something to do with cache hit/miss rates or pipelines rather than the actual code. Even LLVM can not optimize that since it's strongly data dependent.
@@lepidoptera9337C is unsafe because its memory model is unsafe. Unless you use C11 atomics (which didn't even exist prior to 2011), C provides no guarantees about memory access at all. The problem is that every platform/CPU has its own memory rules and you would have to make sure to write C code that follows those rules but since those rules vary by platform/CPU, it's actually not truly possible to write C code that is guaranteed to work correctly on more than one platform. If it does, that's plain luck. Modern programming languages like Rust, Swift, Go and even Java and C# do define a memory model as part of the language that is valid for all platforms the language supports. It's up to the compiler/interpreter to ensure that this memory model works as defined by the language on the platform, no matter what is required to make that happen. The programmer doesn't have to adopt his code to any platform for those, the programmer can rely on what is defined in the language standard.
Here's a very simple example: You have a global variable of type int, whose value is currently 0 and you change its value to 10 in thread A. 30 minutes later, the variable has never been touched ever since, you read the value of the variable on thread B, what will the value be? 10? Show me where the C standard makes that guarantee. Fact is: It doesn't. Prior to C11 C didn't even acknowledge the existence of multiple threads. But even without threads: You only have one thread, that runs on CPU 1 and you write a value, 30 minutes later the SAME thread runs on CPU 2 and reads the value. Again, where does the C Standard guarantee that CPU 2 will see the value that CPU 1 has written? It doesn't, because C does not acknowledge the existence of systems with more than one CPU. And what applies to CPUs also applies to cores of a single CPU. Only when using atomic variables as defined by C11, there now is a memory model and you as programmer can even choose between 6 different memory models but for everything that is not atomic, nothing is defined.
Without atomics, the way you think C works in fact only works if all your code is always running on a single thread only and your system only has one CPU core or if you are lucky and the CPU does make that guarantee in hardware; like x86 does most of the time but PPC, ARM, Alpha, RISC-V, etc. doesn't make those guarantees. That's why so much C code that worked flawless on x86 CPUs fore decades suddenly breaks when it has to run on other CPUs. The code was never correct in the first place but x86 is very forgiving and gives a lot of memory access and ordering guarantees that other CPUs don't. So you basically had C code, that was valid for x86 in particular but that doesn't make it universally valid, as there is no such thing as universally valid C code, because C leaves most definitions to the system, instead of defining it to the language.
This is also true for plenty of other situations. E.g. what happens if a signed int overflows? Not defined in the C standard. What happens when you de-reference NULL? Not defined in the C standard. What happens when you shift a 32 bit integer right by 32? Not defined in the C standard. Of course, on every platform the behavior is defined but the definition varies by platform, that's why modern languages define it as part of the language.
As for performance, I have a C code sample, that calculates a Mandelbrot set. It's as basic as this calculation can get (three for loops, an array of float values, some simple int and float calculations, one static function being called from within the main for loop). When I compile it with clang using -O3, the code runs slightly slower as when I take the code as is, convert it to Rust (which almost requires no adoption, except for the data types and the function definitions) and compile it with the Rust compiler. Reproducible on x86 and on ARM CPUs. If UA-cam would allow external links, I'd show you the code and I take any bet, you cannot make the C code run faster than than the Rust code, as there is nothing you could optimize about that code and there is no better optimization than -O3.
@@lepidoptera9337If I could post links here, I could show you a simple Mandelbrot calculation code that will run faster in Rust than in C and I bet that there is nothing you can do to make this code run faster in C, as this code is minimalistic and there is nothing you could optimize that the compiler cannot optimize 100 times better than you and still Rust always wins on x86 and ARM with both compilers set to maximum optimization.
And C is unsafe because it does not define a memory model (only for atomics which only exists since 2011) and it also has tons of undefined behavior, where the C standard simply does not define what the outcome of an operation will be. This makes it very easy to port C to any platform but it also means that the same code will not behave the same on different platforms. Modern languages have a well defined memory model and well defined behavior and it is the task of the compiler to ensure this behavior on all supported platforms, no matter what is required to make that happen, so you can rely that the same code will produce the same results on any platform.
@3:32 There is a very simple way to "say" that a function will only reference a pointer:
int i_reference(const char *const string); // string is a constant pointer to constant data, aka. immutable
int i_mutate(char *const string); // string is a constant pointer to variable data, aka. mutable.
int i_am_unsafe_read_doc(char * str) // whoever wrote this, had no regard for safety, purge this from your codebase.
All pointers are copied by default in C, because their size is 1 word, so the *const doesn't affect functionality, but shows intent.
C code @3:20 is incorrect. The divide function definition has an integer pointer as first parameter. So when calling the divide function, you pass with & a normal variable not a pointer to the divide function. int a = 20; int return_value = divide(&a, 4); printf("%d
", a);
When's the next video my dude? You've got everything here that makes a great channel, what are you currently passionate about? Tell us about it! 😄
(My only feedback, if I may, would be to use a better mic, don't buy one, just use your phone's microphone like I demo in my "how to sound great" video)
Looking forward to seeing more!
Thank you, that means so much coming from the best! I will definitely follow that advice if I end up making another video. I was having a lot of trouble with my adsense account which was killing my motivation, but hopefully the situation can be resolved in the future. Until then I will learn as much as possible to have some material for the future! ;)
Interesting video with interesting comments, thanks for the upload.
Using clone repeatedly as you pass each string to the greet() function isn't using more memory *each* time, because each instance is immediately dropped inside the function. But anyway as others have said only a Rust rookie would write it that way.
but it still has to allocate each time, which is the biggest overhead with memory
@@dylanmashini Certainly. Nobody would say it was a good approach. I'm just noting that what was stated is technically incorrect, and not the reason it's a bad idea. (Edited to fix typo.)
Well said, thank you for correcting me. Apologies for the mistake in the video!
In fact, the algorithm would probably use the same amount regardless due to memory paging. The string should fit in the same page as the original, so it theoretically would just be calling memadvise and not actually use a full allocation. It’s still slower, but isn’t as bad as it could be unless you’re unlucky enough to do this on a page boundary.
Rust is faster than C++ if you don't know how to write good code in C++
Having to check that the location of an array you're accessing isn't out of bounds means bad code (unless the user is involved, the user will find all possible bugs)
Even division by zero checks (again, unless the user is involved) means bad code
Excellent video, and something I didn't think of before: newer languages have more recent improvements to algorithms built into the standard library. What I think is a bit implicit though in the topic of 'consider Rust over C/C++ for longevity purposes', is a feature that C and C++ have that also harms it a bit: the guarantee of backwards compatibility with each new language release. This means that bad, old parts of the language still have to be included and maintained, rather than pruning bad or obsolete parts of the language and focusing development on better, newer paradigms. This is not the strategy of C and C++ because it would mean that each language revision would break compatibility with older language versions which would strain development.
Newer languages like Rust do not have this problem of having to carry years worth of obsolete features, not least because said features have not (yet) had time to accumulate. This also helps with program development because one needn't sift through a dozen or so options to weed out bad practices that were rendered obsolete by language revisions.
Backwards compatibility is actually something the people behind Rust take seriously and have a beautiful solution: Editions.
We could launch a new version of Rust with breaking changes (say like Python 2 to 3) tomorrow - without breaking anything. Every cargo project knows what edition it's in and the compiler will treat them accordingly. Not only can you compile your ancient legacy code with the newest compiler version but you can combine different libraries written to completely different standards and the compiler will figure it out.
@@swapode That's a beautiful solution indeed, and another reason to love Rust!
2:00 is there any explanation on why we need to clone it? what does it mean by "moved"?
Greet takes the String by value and not by reference. Taking by value moves the variable into the function and it doesn't return out again. It is moved out of scope. Clone explicitly creates a new copy, so that it may be moved into the variable.
In Rust, there are 3 main forms that you can pass a variable by.
You can pass it by value, in which case you are giving over full control of the variable to the function or new variable. The previous variable name no longer refers to that data. The data has been "moved" into the new variable name/into the function.
You can pass it by immutable reference, which lets you look at the data within and do what you want with that data, as long as you promise not to change it. This is super useful as it lets you write functions
Finally you can pass it as a mutable reference, which means that you have rhe ability to change the value, but you promise to give it back once you're done using it.
An important distinction that isnt usually taught at the same time as this concept is about cloning vs copying. To understand that you need to understand that there are two different parts of the program's menu, the stack and the heap. The stack is super fast, but everything you put on the stack needs to have a size thats known when the program's compiled. Things like numbers, characters, and many enums/structs etc. have a size known at compile time. Other things, like Strings, can be various lengths, so you dont know how much datat they'll need until you know what's going in them. These can't go on the Stack, and instead go on the heap, which is slightly slower. Copying can be done on the stack, and because the size of the thing being copied is already known, the bits of the data can just be copied without any slow checks. Cloning, on the other hand, applies to data on the heap, and involves checking the size of the data, finding a spot to put it, and then copying the data across. Much slower.
In some situations, you want to create a second copy of the data, such that the new copy and the original are both accessible. For datat on the heap, clone is well used here. Sometimes though you just want to change some data, or read it for a one off purpose. Passing by reference is better here.
4:20, C++ can return a string empty or with an error message too. But this approach is slow, because it needs to create the string. Faster is to do what C use to do: return an integer, with negative value in case of error. Then, if the user wants to see the error message, it can be fetch in an global array of errors. C can also return a null pointer, in case of success, and a valid pointer to an error message otherwise. But the int8 return is faster.
4:34, C++ can say if a parameter is const or not too. Your example doesn't beat C++ by any means. To be fair, there's 1 case: if they are int pointer and an int, there's a possible performance issue: pointer aliasing. It can be solved by copying the int to a local variable or by using some compiler-specific keyword, such as restrict, but this last solution is not standard yet. This is an annoyance Rust hasn't.
Bro began with stating that Rust is higher in productivity as writing javascript then proceeds to proof it by comparing it to C 💀💀💀
Very nice, may the yt algorithm recommend it more!
Thank you for this info
Cool video! Very interesting!
Thank you!
Simply untrue. Can't wait to see your zero days because rust can be fast or safe; never both :)
No way you brought up team, intentions and our own intentions and had an image of Young Thug.
You sir, have earned a subscriber. Too few of us in this field.
Having the compiler be the sandbox as far as form creation (what memory handling strategy we need for the solution) is great only when you know exactly what the logic of the code is. Good old fashion debugging and sense making (ala Bret Victor interactive infographics) is still necessary for logic checking in the long run.
?
However, Rust's bigger strength is its type system in general. You can teach the compiler all sorts of new invariants to enforce. For example, the typestate pattern lets you teach it to check for correct traversal of state machines at compile time. (The Hyper HTTP library uses it so that things like "tried to set a header after the request body began streaming" are compile-time errors.)
With C++, you need inefficient and verbose runtime checks to match that because, without a borrow checker, there's no compile-time way to prevent code from holding and using stale references if you implement the typestate pattern.
@@ssokolow
I don't really like do defend C++, but I would here for the sake of the reason and truth. In C++ you probably can express typestates (I have seen somes in Github using classes and templates), but in general I would say implementing them in Rust is way easier and safer because of the strictness of the language compared to C++.
@@diadetediotedio6918 That's more or less what I intended to be saying. I've seen typestate implementations in C++ too, but the authors always make it clear that, without a borrow checker, it's possible to hold onto and use stale states or that they have the performance overhead of runtime safety checks.
or u could use for example the "const" keyword in c in the 'divide' function
Appreciate the CC.
Passing by value looks a lot like a unique_ptr, except on the stack. Pretty neat.
Graphics programming in rust is such a pain. Especially vulkan wrappers are not that good its just unsafe code under the hood.
If C is slower it's just skill issue. Besides Rust still doesn't have own ABI...
Interesting, bring more.
I'm relatively new to Rust but I'd say in this example there is no "real" difference between C or Rust in terms of writing the implementation. The Rust code was easily readable to me, even as a beginner, because I am familiar with the syntax (I was screaming "just pass a reference" like 20 times) because thats what I am used to. And tbh the C code seemed to do the same (char *). The difference likely lies within a lack of dedicated Rust libraries C/ C++ had for decades and being forced to handle Results / Options, Err etc. whereas C will let you do whatever. This will be a negative for short POCs but in large code bases you will implement the exact same things that are standard in Rust anyway
hmm, this was very interesting. I do really like that it would force you to explicitly mark mutability. I may consider writing my future code in Rust...
Zig has entered the chat...
Pretty cool video, you deserve way more subs dude!
Rust may be a goid language, but i dont like it. I'll just stay with c
rust without llvm and gcc doesn't even exists and this people are morons XD all their code works with or thru c++ yet they don't like c++ and c cuz they are just bunch of looser typescript developers who never heard of memory managinG XD
their brain cannot just process the new info about compuer science : D
Your explanation is great.
Here is my doubt, I have written a program that has 4 threads between every thread busy waiting on the segment queue. We are creating a packet in one thread and passing via queue to another and processing something in every thread and that packet processed in the last thread and we time stamp at every thread.
Total 1million packet processed.
We have written a similar program in c++ also.
Performance :
MACOS:
rust : 2.9sec
c++ : 2.7sec
LINUX
rust : 15sec
C++ : 3.3sec
Why rust is too slow in Linux. Can anybody help me to solve my problem, I have used every compiler optimization of everything, still nothing happens.
Bro, please don't use clone(). Just borrow the value of name with &String. I wanna cry every time I see that clone() function
Bro great stuff keep it up!, i'll like it all the videos to pump you 😉
Okay I'm officially convinced
Your first example, with the divide function, is not very well thought out/fair. You can make your intentions about parameters very clear in C/C++ by using const pointers or const references. And personally, I like mutable by default.
As for your hashmap example, have you compared every commonly used container that are similar in purpose in both languages (static arrays, vectors, sets, ...)? Otherwise it's just cherry picking on the level of "Look, everyone, I found the one container where the standard implementation of Rust is faster, clearly that means it is better overall!!!".
pov strongest rust propaganda (it's weak).
I wish there was a codecademy-esque place to learn rust, every place I look is like reading a big book and I don’t learn effectively that way
try "rustlings". it's a series of simple exercises to solve that take you through basic rust stuff
Look up rustlings. Installable exercises
In c you can use const keyword for mutability. 😉
lazy people love to whiny 24/7 cuz they aren't capable to manage memory XD
would be nice for the next video to see the performance and productivity comparison that doesn't involve 3rd party libraries
I don't think there is any language compariable to C in my field, that is, directly manipulating hardware, like peripherals and registers. Not even C++.
However in fields at a little higher level than that, C/C++ is definitely not the first thing I would consider. Normally I would put all hardware I/O operations into a dll written with C, and use Python or Rust to handle higher level operations.
It would be interesting to benchmark the performance of C++ Boost in the same manner
You convinced me. Writing my server backend in rust. I was considering c.
Outside of the code writing I believe that Rust is great for teamwork because of the config file, the toml I think. Just type in whatever dependencies you need in there and cargo will do it all for you, all the correct pakages, the correct version of Rust you want and then cargo run, kinda like Python venv. So not only does Rust code is explicit and precise, but the way Rust manages itself is just so good, any team member with Rust installed can just take that toml file and run it, and they are set.
I dislike your example which shows Rust forcing display of intent with its syntax. It's possible to pass via const ref in Cpp too to make it clear if we are modifying the args or not
When’s the next video?
C is always enough. ;-)
This video is convincing me to learn Rust.
U can't just say "when we benchmark" show an actual benchmark and compiler details...
I would bet c usually wins if u choose a good hashmap especially if u optimize for ur data distribution
The only thing I can like Rust to is Bloodborn: frustrating as hell to initially pick up, but once you understand that it’s actively teaching you how to use it just by engaging, it’s so gratifying
Even tho I agree that rust is better at documenting and validating intentions, I feel the "rewrite in rust is easier" logic was fallacious.
That example proves nothing about rust, even if it proves that that particular software in C was a mess.
In Rust you don't need 20 years of experience to try to make safe code
@@JorgetePanete c and c++ ain't for kids to play with it XD go and type in rust crap who doesn't even exist and who cannot evne exist without c/c++ cuz guess what ? it's built upon gcc and/or llvm both are written inc./c++ XD so keep crying kid and go and write your crap language which calls itself RUST XD lolz
3:26 i haven't learnt C, does C actually not have a return type?
C does have return types. However there is not one standard way you handle errors. Unlike Rust resut or option.
One function could set errorno, another could pass back a null ptr, one could send back data you have to pass to an error checking function.
This causes a lot of context switching and searching for the correct way to handle every error. This leads to bugs, slow coding Or lack of error handling.
While C is great In a lot of ways it has trouble allowing/forcing uses to handle errors properly.
I think we should be speaking more about the advantages and disadvantages of each programming language, and thus concluding which is better suited for which situations and why. Yes, C is old, not as friendly as Rust, and can be a headache at times, but it fulfills excellent its original purpose: writing operating systems. As with all technologies, we should know which one to use and when.😄
C is much friendlier and easier to work with than Rust lol.
Or for legacy systems (like 3ds)and micro controllers
Very good video.
Is it just me, or is the audio VERY low?
Yes, the audio is about 6x too silent (8dB).
Checked with my video editor Rem's Studio 😃.
Yeah I apologize for that. I knew publishing the video that the audio was quite bad but I never expected the video to get this much attention, so I didn't think it mattered really. I will have to invest in some better equipment I think.
@@CodeAhead Fair enough. I did enjoy the video. It was informative and cogent. Keep at it.