Quick introduction to Rust for C++ programmers

Hello everyone.

I am a programmer who has about 10 years of experience with C++. Two years ago, I got to know about Rust when trying to find some alternatives for C++ in some big projects, as we often encountered some bugs that took quite a few days to trace the causes, especially then the systems were running in production mode and debugging was not possible. When I first had a look at Rust, I thought it would be a very good choice to solve our problems. However, not until recently do I have time to start learning some Rust programming. At first, I think learning Rust, especially from a C++ background will be quite easy (in fact, many C++ programmers will have the same thought), however it is a bit more difficult than I expected, and I need to invest a significant amount of time in order to get to a quite basic level in Rust programming. Of course, I know learning a total new system programming language is not easy, but in fact learning Rust to me is as difficult to learning C++ from scratch some 15 years ago.

Rust has been becoming more and more popular recently, but a paradox is that not many C++ programmers pay attention to Rust, as far as I know. Many of my friends, who are experienced in C++, either deny to learn Rust, or give it up after a short time, or do not have intention to use Rust for their work. Some even never hear about Rust.

My experience of learning Rust reveals that a C++ programmer can learn (basic) Rust much more quickly than normal if he can extract the similarities between the two languages. Although Rust is quite a radical programming language, we cannot deny that it shares many common ideas with modern C++. Therefore, I create a very short book that compares Rust to modern C++ with very basic examples, one by one in pair. These are very simple examples and are often used to guide programmers when they start to learn any new programming language.

The purpose of the book is by no mean to cover all aspects of Rust, it just gives a very quick introduction to C++ programmers so they can move to write programs in Rust as quick as possible. I expect a C++ programmer will need only about 30 minutes to read through the book and after that he can immediately write some of his first programs in Rust, just for fun first, then after some time of evaluation the features of Rust, he can determine whether to consider Rust to use in serious work. This will be helpful, since at the moment, many C++ programmers even do not want to give a try to Rust, or give up too early because getting into Rust takes more time than they expected.

Here is the link of the book : Introduction · 30 minutes of Introduction to Rust for C++ programmers
Since my knowledge about Rust is quite elementary, there can be issues with terminology and content in the book, so any review or suggestion would be appreciated.

25 Likes

Excellent work! I like that it's a fast paced introduction to the language that compares things quite directly.

In case you wanted to provide other references to your colleagues, a text for a similar audience has been written by @nrc: Rust For Systems Programmers.

It's possibly not worth being overly pedantic about early on in the text, but I was curious about the following statement in your introduction of strings:

Similar to C++, Rust has two type of string data types : the raw character array &str. which is equivalent to char* in C++, and the object String, which is equivalent to std::string in C++.

I'm not a C++ expert. Does char* guarantee bytes encoded as UTF-8? I also wonder if calling &str an array is a good way to begin. Would that start to cause confusion when you introduce slices?

I don't have time to review further currently. If it will be useful, I'll add more notes as progress through your text over the coming days.

4 Likes

Thank you for your review as it would help me to correct the content of the document. The comparison of &str/String in Rust to char*/std::string in C++, is my own observation, and some C++ programmers also have a similar thought . Regarding your question, UTF-8 is supported in C++ 11 with the u8 prefix and this is applied to both char* and std::string. However, the comparison I make here wants to emphasize that char*/&str both point to an array of characters, and String/std::string are the object wrappers of those primitives, which provide methods to manage the memory inside (extending, concatenation, etc). If someone comes from Java or Python, they will feel quite a bit strange to have two types of string in a language and often complain for redundancy or complication, but C++ programmers should immediately understand this if they have a rough comparison. Of course, the comparison is not totally matched, and char* in C++ is in fact equivalent to &[u8] in Rust, but comparing it to &str is my purpose to explain the existence for two types of string. I would make some adjustment in this section so beginners will not encounter confusions with slices section.

4 Likes

I love this idea! I haven't read it yet, but these kinds of resources are great!

A more direct comparison than char * is C++17's string_view, but not everyone keeps up with the latest specs.

4 Likes

Wow, this looks great, @vn.duongthanhtung, and it is really helpful for the community! A lot of people are asking about rust documentation which is more fast paced then the Rust book and which is geared towards people who already know C++.

Here are two more things which I've found both similar and different in C++ and Rust.

  1. Move semantics. Both C++11+ and Rust embrace move semantics, but in C++ a type can lack a move constructor, or may have a default one. In Rust, all types are move-constructable, and move constructor is not customaziable: it's always a bitwise copy. So, for example, std::Vec always does memcpy of the whole buffer, while std::vector often resorts to element-wise move. As a corollary, objects, which require custom move constructors (for example, if some fields of an object are references inside the object itself) are impossible in Rust.

  2. Constness. Rust & and &mut are similar to &const and & in C++. They both have an "escape hatch": in C++, you can mutate mutable fields via const reference to object. In Rust you can mutate data inside of Cell even via & reference. The difference is in the guarantees that references provide. In C++, &const or & is just a promise by the callee "I would not, or I may mutate the data". In Rust &mut is a strong guarantee by the caller that the callee is the only one who can access the data.

3 Likes

Moves don't touch the buffer; only the three words get copied. (pointer, length, capacity)

3 Likes

Yep, I was not clear enough, thanks for pointing out! I mean, during resize, when the capacity of vector is doubled, and it moves element from old storage to new storage, Rust vector can always do memcpy, while in C++ it may not be possible, and even if it is possible, there's no guarantee that the optimizer figures that out.

1 Like

learning Rust to me is as difficult to learning C++ from scratch some 15 years ago.

This is surprising because there's lot of "system level" knowledge that's shared between C/C++/Rust/Ada.

Although Rust is quite a radical programming language,

I don't think so, you should take a look at Idris or ATS2 languages :wink:

I'll read your book later.

Yes, indeed to me learning Rust was more difficult than expected. I moved from C++ to Java in about 2 weeks, and expect something like that for Rust but it was not the case :slight_smile: When you have learned Rust and look back, you will see there are many shares between Rust and C/C++/Ada. However, suppose that you are totally new with Rust and try to read the official tutorial from Rust website, you will see there are many sections with both similarities and new concepts (from C/C++ perspective) combined together, so after reading it for some time, you may get a bit lost . Of course the official tutorial is very detail and well-explained but it is targeted at programmers from all backgrounds, so it will requires them to invest more time to read every chapter carefully. On the other hand, some C++ programmers like me only have some spare time at night and weekend to learn Rust, so the situation is a bit different, we expect to be able to move to Rust as quick as possible, and would like to skip some sections in the official tutorial that are alien to C++ and leave them to a later time. You may imagine something like that : every Saturday and Sunday, we read one or a few chapters from Rust tutorial, after reading them, we find enthusiastic and think we understand the problem well, but the next Saturday or Sunday when moving to new chapters, we may forget the ones reading in the previous weeks :slight_smile: that's why I creates the above document as a summary of basic components of Rust that can help C++ programmers to start to write some programs immediately, and I believe practicing is the best way to learn a new programming language, In fact if we have time to learn Rust in the way we learnt C++ before it will be much more easier, but when free time is limited, we need to choose a better way to learn Rust more quickly.

4 Likes

This presentation springs to mind which is somewhat related https://confreaks.tv/videos/rustconf2017-a-tale-of-teaching-rust

1 Like

There's a substantial gap between between "languages A and B are both systems-level" and "get something useful to compile in languages A and B" :slight_smile:. Beginners would be better off not having any pre-conceived expectations of how long it'll take them to learn Rust, irrespective of their background. Instead, they'll likely absorb some material faster (or slower) depending on what they already know from elsewhere. But that shouldn't drive expectations on pace of learning.

2 Likes

Just read the book and it's definitely a good read for someone like me who comes from that kind of background. I wish I had this at hand when I first started learning rust :slight_smile:

What comes to my mind now is that it would be nice if there was a similar book or a new chapter that explains some best practices to accomplish in Rust what you usually do with OOP in C++. Or even just the basics to learn to "think the Rust way" or something like that :wink:

Regarding the equivalent of Rust slices:

http://en.cppreference.com/w/cpp/string/basic_string_view

Your Rust examples with strings show some confusion between byte-based handling of UTF8 and a more codepoint-based handling.

Yes, I already updated the substring example in Rust to handle codepoint-based for UTF-8. In fact, C++ built-in substr uses byte-based handling for UTF-8 string, but it still better to show the right way of handle UTF-8 in Rust.

The ownership page is somewhat misleading- the Rust example does not heap allocation the Person object, while the C++ examples does. For someone coming from C++ the implication that the Rust examples does do heap allocation would be rather off-putting.

A better comparison would be to std::move the Person directly, without involving unique_ptr at all (or else to use Box in the Rust example). (The same thing applies to the lifetime page.)

2 Likes

Yes, you're absolute right. In fact there is no equivalent to Rust ownership in C++ and unique_ptr is often considered to be the "nearest" implementation that shares the idea. C++ unique_ptr is heap allocated and cannot compared to Rust stack allocation of ownership, it should be more equivalent to Box pointer in Rust. However, in this chapter, I want to use C++ unique_ptr to explain the mechanism how Rust ownership works, (I would rather use something more equivalent but we know there is no such thing in C++). If we do not use unique_ptr to simulate this example and use stack variable, the segmentation fault will not occur since the stack variables only get freed at the end of the function call and nothing wrong will happen. I will put a highlight note in these chapter to emphasize to readers that this is just the mechanism to explain how some concepts in Rust work, and there is no real equivalent to Rust ownership in C++

1 Like

I don’t think you should worry about a segfault occurring or not - showing where the memory is freed is sufficient. After all you might not get a segfault even in C++ code - that depends on whether the access is going to be to a page that’s unmapped (or protected) and how pages are managed are up to the allocator. A segfault is actually a great outcome in those circumstances - the nasty bit will be when it doesn’t happen and you end up scribbling over some memory (ie corrupt) instead.

1 Like

The segfault won't occur, but the std::string will be empty. It would probably be better to point out the difference more directly rather than trying to hide it behind unique_ptr's behavior, since std::move does apply directly to Person objects even if the compiler still allows it to be accessed afterward.

Yes, I already updated the document without using unique_ptr, so it will not cause misleading to new Rust programmers. The segmentation fault will not occur but the output result will be unexpected. Thanks for your correction.

1 Like

I added a chapter about OOP in Rust. However it is quite basic (since all chapters are intended to be read in about 30 minutes) so you may already know all about the things mentioned in this chapter :slight_smile: In case you want to know more about OOP in Rust, there is already a very popular book in our forum by carlomilanesi and here is the link to the OOP chapter in his book https://carlomilanesi.gitbooks.io/rust-programming-step-by-step/content/object-oriented_programming.html

5 Likes