Live data from Hacker News

C++ to Rust Phrasebook

cel.cs.brown.edu

51–60 of 80 posts

Re: C++ to Rust Phrasebook

#51
post #25

One of the common pitfalls I've seen in my time is someone writing a language they are familiar with in a language that just doesn't fit; trying to apply idioms that flow well with one language to another language where that's just not a good way to achieve the same ends. An example I've seen a lot is a C thinker writing C++ classes with an init() function; sure, it works, but the C++ way is to do that in constructor…

The worst pitfall is Rust references == pointers. They are implemented as pointers, but their role is to give temporary (often exclusive) access that is restricted to a statically know scope, which is pretty specific and fits only some uses of some pointers/C++ references. In C++ pointers typically mean avoiding copying, but Rust references avoid storing/keeping the data. When these goals don't overlap, people get st…

>their role is to give temporary (often exclusive) access that is restricted to a statically know scope, which is pretty specific and fits only some uses of some pointers/C++ references

You could have a vector of references to heap allocated data, as long as the references were parametrized by the same lifetime. You might do this if implementing a tree iterator using a vector as a stack, for instance. That goes beyond a statically known scope. But implementing a mutable iterator the same way would require a stack of mutable pointers (and therefore unsafe code whenever you dereference them), since mutable references have to be unique. That does seem like a bad limitation.

Re: C++ to Rust Phrasebook

#52
The discussion of traits vs. classes glosses over a major difference - Rust traits have no associated data, and you cannot access the data of a parent trait. Trying to do object-oriented programming in Rust quickly leads to a mess. This is a huge problem if you try to write C++ in Rust.

There's no mention of ownership at all.

> Many libraries in Rust will offer two versions of an API, one which returns a Result or Option type and one of which panics, so that the interpretation of the error (expected exceptional case or programmer bug) can be chosen by the caller.

Huh? Usually, in Rust you just put .unwrap() or .expect() on a function call that returns a result if you want a panic.

More generally, most of the differences between Rust and C++ relate not on how to write imperative code, but how to lay out connections between data structures. Most hard problems in Rust relate to ownership design. That's true in C++, too, but you don't discover them until the program crashes at run time.

Re: C++ to Rust Phrasebook

#53
post #7

There are so many different flavours of C++ put there that this guide doesn’t exactly do itself the credit it deserves. There are easy ways to implement stuff like enums with members in C++, just put an anonymous enum inside a class/struct, its possible but marked as not possible. Likewise when discussing modules in rust while completely ignoring the existence of modules in C++ that is actually support by modern tool…

Are C++ modules actually production ready now? Last I checked, they still weren't properly supported accross all major compilers.

No…

Re: C++ to Rust Phrasebook

#54
post #23

Earlier quoted context omitted.

How would you deal with fallible construction of objects while avoiding exceptions in idiomatic C++?

The standard idiom is to have a sentinel state for the object indicating it is invalid. For objects without trivial destructors or which may be read after being moved-from (a valid behavior in some systems code contexts) then you need a sentinel state anyway because moves in C++ are non-destructive. C++ uses deferred destruction as a standard tool to solve a variety of problems.

> which may be read after being moved-from (a valid behaviour in some systems code contexts)

std::move as applied to standard library types will leave the object in a "valid but unspecified state".[1] If you're leaving the object in an invalid state (one where the invariants are broken), you're not writing idiomatic C++.

[1] https://en.cppreference.com/w/cpp/utility/move.html

Re: C++ to Rust Phrasebook

#55
post #18

I noticed that for most of the examples the Rust version is more verbose.

And they even made the C++ version more verbose than it should have been. Most people would write:

class Person { int age = 0; };

I wish rust would make default struct field values this easy to write.

Re: C++ to Rust Phrasebook

#56

Earlier quoted context omitted.

The standard idiom is to have a sentinel state for the object indicating it is invalid. For objects without trivial destructors or which may be read after being moved-from (a valid behavior in some systems code contexts) then you need a sentinel state anyway because moves in C++ are non-destructive. C++ uses deferred destruction as a standard tool to solve a variety of problems.

So the existence of an object of the type does not act as a static proof that the state is valid?

This is correct (and I am using “invalid” here in a semantic sense, it is still structurally valid). There are a number contexts in low-level systems code where a static proof is not possible even in theory, so there needs to be a way for code to inspect object validity at runtime. Process address space isn’t entirely private, external actors that your process doesn’t entirely control can modify it e.g. via DMA.

The C++ compiler largely assumes that such static proof is possible by default and has no way of knowing if it is not. To address this, the C++ language has added features for annotating objects to indicate that static proofs of state are not possible at compile-time (e.g. std::launder).

Database kernels are the most extreme example of this because most objects in the address space don’t own their memory address and the mechanism that temporarily puts an object at a particular memory address is not visible at compile-time. Consequently, object location and state has to be resolved dynamically at runtime.

Re: C++ to Rust Phrasebook

#57
post #54

Earlier quoted context omitted.

The standard idiom is to have a sentinel state for the object indicating it is invalid. For objects without trivial destructors or which may be read after being moved-from (a valid behavior in some systems code contexts) then you need a sentinel state anyway because moves in C++ are non-destructive. C++ uses deferred destruction as a standard tool to solve a variety of problems.

> which may be read after being moved-from (a valid behaviour in some systems code contexts) std::move as applied to standard library types will leave the object in a "valid but unspecified state".[1] If you're leaving the object in an invalid state (one where the invariants are broken), you're not writing idiomatic C++. [1] https://en.cppreference.com/w/cpp/utility/move.html

I am using “invalid” here in the semantic sense of not containing a meaningful value. It is not invalid in a structural sense.

Re: C++ to Rust Phrasebook

#58
post #15

Earlier quoted context omitted.

Visual Studio and Clion are the ones currently with the best experience regarding modules. And in VS could be much better, but EDG has other priorities. VSCode isn't really that great option for C/C++.

I prefer vscode simply because VS is excruciatingly slow. e.g. the file open pane in vscode pretty much instantly lists the file I'm looking for, while the counterpart in VS (ctrl+,) takes several seconds and intermixes search results for files and file contents, when I'm only interested in files.

DPack (free) has a useable file browser. I use Visual Assist (commercial) these days and that has one too. Both pop up pretty much instantly.

Both available from the extension marketplace.

Re: C++ to Rust Phrasebook

#59

Earlier quoted context omitted.

So the existence of an object of the type does not act as a static proof that the state is valid?

This is correct (and I am using “invalid” here in a semantic sense, it is still structurally valid). There are a number contexts in low-level systems code where a static proof is not possible even in theory, so there needs to be a way for code to inspect object validity at runtime. Process address space isn’t entirely private, external actors that your process doesn’t entirely control can modify it e.g. via DMA. The…

Definitely agree that there's plenty of cases in systems code where static proofs are impossible. That makes it all the worse when you give up on static proofs in places where they are possible.

Re: C++ to Rust Phrasebook

#60
post #37
post #28

Earlier quoted context omitted.

So you are saying that this approach teaches C++ users to use the wrong idioms in Rust?

Wrong is too strong. The code is okay given the constraint — this is a guide for C++ programmers thinking in C++ terms, not for teaching purely idiomatic Rust from the ground up.

Which, as a cpp programmer trying to pick up Rust, is honestly fine to begin with. Once you've written varying amounts of code in 5-10 programming languages, it is incredibly tedious to flip through pages trying to teach you how if conditions work and how for loops work: my brain doesn't pay attention even if I try.

This is more like: how to survive rustc as a cpp programmer which is honestly your mindframe when you start out, and it sets you up for "okay now that you speak the syntax, this is how to really think in rust terms".

Post reply on HN