Announcing Arti, a pure-Rust Tor implementation (Tor blog)
to try bring Arti to a production-quality client implementation over the next year and a half". The C implementation is not going away anytime soon, but the idea is that Arti will eventually supplant it. The project sees a number of benefits from using Rust, including:
For years now, we've wanted to split Tor's relay cryptography across multiple CPU cores, but we've run into trouble. C's support for thread-safety is quite fragile, and it is very easy to write a program that looks safe to run across multiple threads, but which introduces subtle bugs or security holes. If one thread accesses a piece of state at the same time that another thread is changing it, then your whole program can exhibit some truly confusing and bizarre bugs.But in Rust, this kind of bug is easy to avoid: the same type system that keeps us from writing memory unsafety prevents us from writing dangerous concurrent access patterns. Because of that, Arti's circuit cryptography has been multicore from day 1, at very little additional programming effort.
Posted Jul 9, 2021 20:46 UTC (Fri)
by mss (subscriber, #138799)
[Link] (19 responses)
Posted Jul 13, 2021 12:31 UTC (Tue)
by sandsmark (guest, #62172)
[Link] (18 responses)
Has anyone mapped and counted all the transitive dependencies that are now a direct concern for Tor?
And they trade a new, unproven code base in a language with relatively less understood concerns (except the growing supply chain attacks to my understanding) and tooling for a relatively battle-hardened codebase.
As always I'm grumpy and conservative but think gradually rewriting in modern and memory-safe (and thread-safe, you could even implement your own typesystem replicating the native rust approach) C++ is more sane.
That way you don't need to throw out all the tested and verified code you have, but can gradually modernize the code with more safe primitives.
Posted Jul 13, 2021 16:26 UTC (Tue)
by mathstuf (subscriber, #69389)
[Link]
Posted Jul 13, 2021 23:11 UTC (Tue)
by roc (subscriber, #30627)
[Link] (9 responses)
Posted Jul 14, 2021 6:06 UTC (Wed)
by ncm (guest, #165)
[Link] (8 responses)
Posted Jul 14, 2021 11:06 UTC (Wed)
by roc (subscriber, #30627)
[Link]
Posted Jul 14, 2021 15:28 UTC (Wed)
by peter-b (subscriber, #66996)
[Link] (6 responses)
Hi, C++ standards committee member here.
You can't get "equivalent safety" in C++ to Rust "by other means", because the Rust programming language can express abstractions that C++ cannot, and because Rust has different and irreconcilable semantics with respect to references which are necessary for the compile time checks that the Rust compiler performs.
If the type of "correct by construction" programming model that Rust provides is appealing, then I recommend adopting Rust. Insisting that C++ has equivalent safety is silly, because it does not.
Posted Jul 15, 2021 5:41 UTC (Thu)
by ncm (guest, #165)
[Link] (5 responses)
C++ can express abstractions that Rust (still) cannot. But both languages are evolving rapidly.
Posted Jul 15, 2021 6:21 UTC (Thu)
by roc (subscriber, #30627)
[Link] (1 responses)
Posted Jul 18, 2021 21:53 UTC (Sun)
by tialaramex (subscriber, #21167)
[Link]
But we are on the Internet, and so it is of course also possible ncm is a dog (I have verified that the committee member was not a dog).
In the former case, maybe we can say Nathan is trying to get C++ to a better place. After all, I think implicit constructors were a bad idea, they're the wrong default, but they're only a default at all because of the "explicit" keyword, which is apparently Nathan's idea. So once the situation was "C++ constructors are inexplicably dangerous" and Nathan improved it to "C++ constructors are inexplicably dangerous by default". The correct fix (an "implicit" keyword) violates C++ backwards compatibility promises and (which of more practical upshot) breaks a bunch of working code. So "explicit" means as long as every C++ programmer is conscientious and never makes a mistake they avoid this particular footgun. "Hooray".
Posted Jul 15, 2021 13:42 UTC (Thu)
by mathstuf (subscriber, #69389)
[Link]
The one I know of is "template template" parameters (basically higher kinded types). I suspect concepts can get one higher-ranked trait bounds as well, but I'm not sure about that. FWIW, both of these are being worked on.
On the other hand, C++ lacks useful destructuring (pattern matching is being worked on, but I don't see it being anywhere near as ergonomic), is stuck with terrible macro expansion rules, bad move semantics, and a blissful unawareness of lifetime analysis at the language (as opposed to the documentation/review) level.
I know which set *I* find more useful, but that's obviously not all that universal since there are many niches in the programming space.
Posted Jul 15, 2021 17:11 UTC (Thu)
by rgmoore (✭ supporter ✭, #75)
[Link] (1 responses)
This is trivially true, since one of the goals of Rust is to make it difficult or impossible to express some kinds of bugs. Greater expressivity is desirable only to the extent the things you're expressing are themselves desirable. Adding lots of foot guns makes the language more expressive, but in a way that's likely to make the final output worse rather than better.
Posted Jul 16, 2021 22:30 UTC (Fri)
by marcH (subscriber, #57642)
[Link]
C++ is safe, you're just "holding it wrong". Well, too bad a couple lines of code "held wrong" are enough for a vulnerability or elusive concurrency crash.
Standard committee-level discussions matter, but how that translates into what happens in the trenches matters even more.
Posted Jul 13, 2021 23:36 UTC (Tue)
by tialaramex (subscriber, #21167)
[Link]
That's a completely different set of skills. But wait, their core goal is they'd like a concurrent Tor. "Modern C++" still isn't very suitable for that. So, you propose they should "implement your own typesystem replicating the native rust approach" in order to have thread safety. So now it's an architecture astronaut project "Somehow replicate Rust in C++" when all they actually wanted was to write concurrent software, something Rust is famously good for.
You're right to be at least a little concerned about supply-chain attacks, but there Rust actually shrinks your window of concern. An apparently harmless C library can sneak in a backdoor pretty easily almost anywhere, but in Rust you'd have your work cut out. The biggest opportunity is procedural macros. Rust's declarative or "by example" hygienic macros aren't very dangerous, but procedural macros are powerful. Today arti uses a LOT of little crates and I agree it would be beneficial if it used fewer, better known, big crates that solve big problems and chose to copy-paste small useful ideas rather than pull in a whole crate for each one. And then sure, you would need to audit proc macros and some types of unsafe code in the crates used before you could trust the whole product.
Posted Jul 14, 2021 23:40 UTC (Wed)
by mss (subscriber, #138799)
[Link] (5 responses)
These all try to re-implement the distro package repository with something which is often much less curated and supervised - clearly these are optimized much more for ease of use rather than quality or security.
But this is mostly a different issue from whether the code itself is C, C++ or Rust.
Posted Jul 15, 2021 6:10 UTC (Thu)
by roc (subscriber, #30627)
[Link] (4 responses)
When I'm writing software I'll decide what libraries and what versions of libraries I want to depend on. I'm not going to ask the Ubuntu maintainers to decide which libraries are OK for my project.
Posted Jul 15, 2021 23:14 UTC (Thu)
by mss (subscriber, #138799)
[Link]
> When I'm writing software I'll decide what libraries and what versions of libraries I want to depend on.
Distros sometimes (often?) need to build and run a package against different library versions than its upstream supports (for various reasons).
Posted Jul 15, 2021 23:46 UTC (Thu)
by pizza (subscriber, #46)
[Link] (2 responses)
Sure, you need pyFoo == 1.33.1 (or whatever)
Meanwhile, pyFoo includes a big pile of native C (or Rust, or whatever) code that itself has other dependencies.
How are those dependencies and sub-dependencies handled? (The answer: Quite badly, as in "It only works on a specific Ubuntu point release." Go ahead, ask me how I know this)
These language-specific repositories are great, until they inevitably have to reach outside the language ecosystem. Then it's hack upon hack until suddenly the distro system library/dependency model doesn't look so bad after all.
Posted Jul 16, 2021 0:20 UTC (Fri)
by roc (subscriber, #30627)
[Link] (1 responses)
Rust libraries that wrap a C library usually come with a "build script" (build.rs) that is capable of downloading, configuring, building and linking the C library. This works very well in practice, and is not a hack at all. The cc library (https://crates.io/crates/cc) gives you a nice API to help with this. A lot of these Rust libraries also give you the option of using the system version of the library if you want.
If the C code itself has dependencies that aren't available on the system then that would get complicated. I haven't encountered this problem in my Rust work. If I did, I guess I'd probably choose a different library.
> suddenly the distro system library/dependency model doesn't look so bad after all.
No, the model where distros package all libraries simply doesn't work at all for me, and lots of other developers, for the reasons I mentioned.
Posted Jul 22, 2021 10:42 UTC (Thu)
by immibis (subscriber, #105511)
[Link]
Posted Jul 11, 2021 6:17 UTC (Sun)
by ncm (guest, #165)
[Link] (83 responses)
Another way to say this is that coding any sort of shared access in Rust is made artificially very hard, so that among all the myriad ways to code it that would work correctly and the other ways that would not, the compiler rejects all but one, and your task is to puzzle out exactly what that one is.
It is not accidental that the one way *does* work. But "easy" is in no wise a scrupulously accurate way to describe the process.
The satisfaction one gains in having solved the artificial puzzles the language imposes on top of each programming task can be easily mistaken for actual pleasure in using the language. This quirk of human psychology may be seen in many other activities, wherever unnecessary difficulty is treated as a virtue. The fanaticism that tends to result is the same in all cases.
Posted Jul 11, 2021 8:51 UTC (Sun)
by ibukanov (subscriber, #3942)
[Link] (17 responses)
Given that all popular programming languages have own idiosyncrasies like a pleasure of debugging rare memory safety bugs in C++ or backporting Python code from older deprecated versions or refactoring huge JS code base without type annotations, the same can be said about all of them. The pleasure comes from the feeling of winning on the compiler/debugger/runtime, not from the language itself.
Posted Jul 11, 2021 10:47 UTC (Sun)
by khim (subscriber, #9252)
[Link] (13 responses)
Situation with Rust is different. Cases where compiler just doesn't know enough about your code to understand that it's valid are numerous, but after few months you just fix them automatically by adding lifetime annotations without even thinking much. But cases where you don't know how to easily silence the borrow-checker… and need to “fight” it for real… 9 times out of 10 it's cases where compiler discovers some hidden problem in your design! Which would stay as source of hidden bugs for months or maybe years in most other languages! And for the remaining one case there are always That is why it's most loved language. Yes, it's hard to write code in it — especially for newbie. But when you realize that it's because it does a lot to make sure your would spend minutes or hours fighting the compiler instead of weeks or months sitting in debugger… your attitude changes. Significantly.
Posted Jul 12, 2021 2:47 UTC (Mon)
by ncm (guest, #165)
[Link] (8 responses)
In my case, in the past decade I have spent strictly more time preparing compiler bug reports than in debugging memory usage errors. Rust, thus, could not possibly have saved me more than exactly that amount of time. Had I been coding Rust, instead, besides probably reporting many more bugs in its (then) immature compiler, I would have spent hundreds or thousands of times that long waiting on its (still today) very, very slow compiler. Much of that time would be in waiting to see, again, whether the compiler was yet satisfied with my lifetime annotations.
Thus, while it is easy to believe that using Rust reliably saves *some* programmers the time they would otherwise have spent on bugs they would have coded in another language, it is nowhere near correct to say it offers any such benefit to all.
The language's chief benefit might be, instead, for people using code written in it, reducing their worries about the quality of code they must run that is written by programmers whose diligence and care they have no good reason to trust, or have good reason to distrust. Those programmers, then, are in effect donating all the extra time they spend waiting on the Rust compiler for the peace of mind of that user. I am often that user, and I do not mind running programs coded in Rust, howsoever glad I am that I was not obliged to code them in it myself.
Posted Jul 12, 2021 5:44 UTC (Mon)
by xophos (subscriber, #75267)
[Link] (5 responses)
Posted Jul 12, 2021 16:56 UTC (Mon)
by ncm (guest, #165)
[Link] (4 responses)
I am also very grateful for help the compiler gives you.
Posted Jul 13, 2021 7:23 UTC (Tue)
by immibis (subscriber, #105511)
[Link] (3 responses)
Posted Jul 14, 2021 6:15 UTC (Wed)
by ncm (guest, #165)
[Link] (2 responses)
The practice of writing code in C++ is wholly different from writing C code. In particular, writing C++ code without introducing subtle memory errors is orders of magnitude easier, and mistakes in C++ are orders of magnitude easier to spot (i.e., just look for code that looks like C), and not at all tempting.
Saying "C/C++" is meant to mislead by implying a false equivalence.
Posted Jul 14, 2021 11:08 UTC (Wed)
by roc (subscriber, #30627)
[Link]
Posted Jul 22, 2021 10:43 UTC (Thu)
by immibis (subscriber, #105511)
[Link]
> So you are either one of the very rare programmers whose mental capacity is sufficient to write C or C++ code without introducing subtle memory errors.
These are both languages where it's possible to introduce subtle memory errors, and the statement suggests that the person in question is capable of understanding their program's memory management well enough to not introduce them.
Posted Jul 12, 2021 9:02 UTC (Mon)
by khim (subscriber, #9252)
[Link]
Were these bug reports accepted and fixed? Or were they closed because, apparently, you have triggered some kind of “undefined behavior” and thus compiler writers closed these bug reports as “invalid”? In my experience it's not that hard to write correct code in C, C++, Python or any other sane language (not Barinfuck) as long as you are working alone (except where your idea of “undefined behavior” doesn't match the idea of “undefined behavior” of compiler writers). But when you start changing code whose design is not yours… you start breaking it. Simply because you don't know it by heart. And that is when you start spending more debugging things than writing them.
Posted Jul 12, 2021 15:22 UTC (Mon)
by Hattifnattar (subscriber, #93737)
[Link]
Also, do yo parallelize your code (a) never, (b) as the last resort, or (c) every time there is even a chance it will benefit the performance?..
Posted Jul 12, 2021 9:19 UTC (Mon)
by NAR (subscriber, #1313)
[Link] (1 responses)
It sounds similar to dialyzer results on Erlang code. Dialyzer is always right, so when the programmer doesn't understand its complaints, it's usually a real bug. On the other hand, in my experience these bugs are 95% on (not tested) error handler code paths or simply unused/unreachable code, because most of the real bugs are found by tests.
Posted Jul 12, 2021 11:19 UTC (Mon)
by khim (subscriber, #9252)
[Link]
But error handling is precisely where such things are important. Precisely because these are rarely tested yet often are lucrative targets for attackers. Rust developers have tendency to just plug these with
Posted Jul 12, 2021 11:34 UTC (Mon)
by ballombe (subscriber, #9523)
[Link] (1 responses)
Posted Jul 12, 2021 14:12 UTC (Mon)
by khim (subscriber, #9252)
[Link]
Nope. Haskell is ahead of C/C++ or Java, of course, but significantly behind C#, Go, Python or Swift. Look for yourself. In case of C or C++ one can explain that it's because people are forced to use the language they hate, but I don't think anyone is forced to use Haskell (at least I haven't heard about such cases). Rust have become “most loved” after it's developers proposed stability without stagnation course. And while “after” doesn't mean “because” I suspect it's one very important thing which distinguishes Rust from Haskell. While modern GHC-Haskell is immensely power language… it has no good tutorials and when you try to learn it and find out that you couldn't even type examples from existing “recommended” books and run them without modifications… it's frustrating… compare it with The Book which says, right now, This version of the text assumes you’re using Rust 1.50 or later with edition="2018" in Cargo.toml of all projects to use Rust 2018 Edition idioms. Although everyone admits that Haskell is more powerful language at the moment (heck, even Haskell 98 includes higher-kinded types, which Rust lacks today) but from practical POV good async story is more important than higher-kinded types — that's why Rust tries to build solid async story while higher-kinded types remain an experiment…
Posted Jul 12, 2021 1:02 UTC (Mon)
by ncm (guest, #165)
[Link] (2 responses)
The essential difference from the other examples cited is that the difficulty overcome in those cases is not artificially imposed by the design of the language. The craft of programming is filled with abundantly multifarious technical challenges; it could almost be defined by them. But most usually, challenges are imposed by the problem domain; and we like a language best for how it keeps out of our way, not for its imposing extra difficulties on top of what the problem provides.
The other place in programming where we see the particular flavor of enthusiasm seen (in some) for Rust is around Forth, described as "the first Programming Religion". In Forth, the built-in hurdle to be repeatedly overcome (IIUC) is arranging that the values to be operated upon are on top of the evaluation stack exactly when they are needed, and so need not be named. Forth's affinity to the native primitives of computation make elegantly expressed Forth also approach the most efficient possible sequence of operations, and thus approach a sort of Platonic ideal.
We might thus call Rust "the Second Programming Religion". In saying so, we should be careful to distinguish enthusiasm for the many good qualities of the language design (typically enabled by leaving behind old mistakes) from the fanaticism of the Convert. The latter can be perceived most readily in its adherents' need to disparage other languages, to exaggerate the practical value of specific details and the practical importance of the language, and to minimize emergent flaws perforce retained for backward compatibility.
Posted Jul 12, 2021 22:50 UTC (Mon)
by notriddle (subscriber, #130608)
[Link]
Fourth. The first is Forth, the second is LISP, and the third is Smalltalk.
Posted Jul 20, 2021 8:55 UTC (Tue)
by anton (subscriber, #25547)
[Link]
Posted Jul 11, 2021 8:52 UTC (Sun)
by Sesse (subscriber, #53779)
[Link] (12 responses)
Posted Jul 12, 2021 3:57 UTC (Mon)
by ncm (guest, #165)
[Link] (11 responses)
Making wrong things hard to express is valuable where we are tempted to wrong things. Thus, we would like for people tempted to wrong things to learn Rust. Our problem is that people tempted to wrong things are typically not tempted to learn Rust.
Posted Jul 13, 2021 16:51 UTC (Tue)
by NYKevin (subscriber, #129325)
[Link] (10 responses)
That's not a bug, that's a feature. If you write your app in Rust, you will filter out those people automatically.
Posted Jul 13, 2021 18:22 UTC (Tue)
by rgmoore (✭ supporter ✭, #75)
[Link] (9 responses)
This depends on how you look at it. If you are only interested in one project, filtering out bad programmers seems like a good idea-- so long as you can still get enough good programmers to do the job. But if the goal is to improve programming as a whole, having the people who most need the benefits of a new approach be unwilling to use it is unfortunate.
Posted Jul 17, 2021 20:52 UTC (Sat)
by marcH (subscriber, #57642)
[Link] (8 responses)
BTW Rust is of course not the only memory safe language and this "no memory corruption" trend has started already, after all Java/Kotlin, Javascript, Swift and golang are already very popular. What's new with Rust is filling the last gap where C and C++ had no serious competition. That's why its zealots feel threatened.
Posted Jul 17, 2021 21:31 UTC (Sat)
by pizza (subscriber, #46)
[Link] (7 responses)
I'd love to see an actual citation for that "70% of all security incidents" thing.
For example. The largest data breach in history (Equifax) was due to an unpatched Apache Struts deployment, written in "no memory corruption" Java.
Phishing and general credential theft? Social engineering, not memory corruption.
Posted Jul 17, 2021 22:34 UTC (Sat)
by ms-tg (subscriber, #89231)
[Link] (6 responses)
> I'd love to see an actual citation for that "70% of all security incidents" thing.
Here's the citation I am aware of:
* Matt Miller, an engineer at Microsoft, presented the following talk at the conference Bluehat 2019 in Israel:
As an aside: this appears to be the same conference at which Andrew "bunnie" Huang delivered the talk "Supply Chain Security: If I were a Nation State…", which I think I also read about here at LWN.
It appears that the citation for the 70% statistic starts at 14:05 into the talk, which corresponds to Slide 11.
Graph shows a period of 12 years, 2006-2018, showing that around 70% of "security vulnerabilities addressed by a security update" each year correspond to a memory safety issue. Subsequent slides break those down further into classes of memory safety issues.
Hope this helps!
Posted Jul 17, 2021 22:43 UTC (Sat)
by ms-tg (subscriber, #89231)
[Link]
* Microsoft Security Response Center: A proactive approach to more secure code
The connection between the cited statistic, and Microsoft's exploration of the Rust language, appear to have been widely covered in all sorts of media, for example:
* https://www.zdnet.com/article/microsoft-to-explore-using-...
Posted Jul 18, 2021 1:39 UTC (Sun)
by pizza (subscriber, #46)
[Link] (3 responses)
Ah, okay, so that's "70% of security vulnerabilities" -- which is *not* the same as "security incidents".
(As the 7th & 8th slide in that deck demonstrates, there is a considerable gap between "vulnerabilities" and "exploits", and the 9th slide says the "market" has moved predominantly to social engineering-based attacks instead)
Posted Jul 18, 2021 16:48 UTC (Sun)
by marcH (subscriber, #57642)
[Link] (2 responses)
Is the former acceptable?
Posted Jul 21, 2021 17:27 UTC (Wed)
by pizza (subscriber, #46)
[Link] (1 responses)
"Acceptable" is just a point on the cost/benefit curve.
Clearly it has been acceptable. And, to be blunt, it will continue to be, for the same reason that the financial industry still runs on COBOL -- Rewriting the billions of lines of existing "inherently unsafe" code into "memory-safe" languages will cost a *lot* more money (and introduce far more new bugs along the way) than the current practice of (semi-proactively) plugging holes and cleaning up messes after the fact.
The costs of "security vulnerabilities" are nearly always external (ie someone else's problem). That vulnerability only becomes an "incident" is when an organization directly incurs some cost. Until then, it will be ignored unless an external entity (eg government regulation or payment card or insurance carrier requirement) forces an organization to proactively care.
Posted Jul 21, 2021 22:25 UTC (Wed)
by marcH (subscriber, #57642)
[Link]
> "Acceptable" is just a point on the cost/benefit curve.
Sure, what I meant was: should anything at all be done about memory corruption? The answer is yes of course - and it is being done in some places and not just with Rust. Exciting times.
> Until then, it will be ignored unless an external entity (eg government regulation or payment card or insurance carrier requirement) forces an organization to proactively care.
In general yes of course but there a few exceptions like Microsoft, Google and a few other "SmallTech". Check the Microsoft slides linked above.
Posted Jul 19, 2021 2:36 UTC (Mon)
by marcH (subscriber, #57642)
[Link]
Among others it explains why attacks rely less often on vulnerabilities and more on other techniques like social engineering. It's not because there was much coding and quality progress, in fact it rather depressingly states that developers keep making the same mistakes as before. The main reason is because Microsoft fixes them much faster than ever and made it very difficult to turn off auto updates. This gives a very strong incentive to minimize usage of vulnerabilities and reserve them for discrete, targeted attacks. Auto-updates have basically changed the market of vulnerabilities, their value has increased to the point where only nation states or very rich actors can afford them. I'm over simplifying of course, open the PDF if you're interested in the actual numbers.
So I stand corrected: the connection between ransomware and memory safety is probably tenuous... _if_ you use aggressively auto-updated software exclusively!
https://www.theverge.com/2021/1/6/22217052/microsoft-wind...
As long as you're not a high profile target or you don't mind getting spied on by any government or other powerful actor, keep using software written in a memory corruption language. Just make sure there's a billion dollars company constantly racing to keep it up to date.
Also keep in mind this analysis is based on Microsoft's data only.
Another interesting point is how effective but still limited mitigations like CFG, CET,... are and how the only way to make them 100% effective "decomposes to needing to solve memory safety".
Posted Jul 11, 2021 10:33 UTC (Sun)
by khim (subscriber, #9252)
[Link]
Shared access is hard. Intrinsically. It's hard in C, Java or even if you just directly write the machine code. Simply because when one variable have two owners than none of them can ever be sure they know what value is stored in that variable at any given time. You have to have some kind of protocol which ensures synchronization (there are bazillion articles on LWN alone which explain how you can do that in different cases). Language which makes that notion explicit and makes the compiler do the verification for you in 99% of cases is boon, not trouble. And for cases where you really need shared access there are always
Posted Jul 11, 2021 13:33 UTC (Sun)
by flussence (guest, #85566)
[Link]
Posted Jul 12, 2021 1:02 UTC (Mon)
by tialaramex (subscriber, #21167)
[Link] (47 responses)
Was I just much better at programming than I realised all this time? Were the (numerous) people expressing such opinions outliers who are amplified beyond their true numbers?
For a while I read material suggesting that these takes are inspired by earlier Rust versions where the compiler is much less able to infer your intent. If you learned Rust a few years ago there were a lot of places where you must spoon feed the compiler, spelling out very obvious things about your program that today's compiler just gets right. So perhaps people with these takes are just responding to an infelicity which has since been eased, and similar improvements continue today (e.g. arrays got nicer in 1.53). But I don't think that's it.
I think it's a little of that first thing I mentioned but in a very narrow way. All this time I've had this concept of ownership for non-trivial objects, in languages like C that didn't really express it. So, to me this behaviour in Rust matches the mental model I already had. Of _course_ it matters whether this QuadResourceAnswer owns the Quads or just refers to them, and now that my programming language actually cares as much as I did that's actually reassuring. Of _course_ it matters whether the thing we matched in the String lives as long as the String or the Regular Expression doing the matching and again it's reassuring that now my programming language cares too.
If you're the sort of programmer who was excited about Owner<> (which doesn't actually _do_ anything on its own) in the C++ support library, and who has strong opinions (whatever they are) about whether raw pointers are owning, or not owning, you're more likely to think Rust just makes sense, and these responses are crazy.
Whereas I suspect that for ncm and many of those who see Rust this way, it's a bicycle for fish. They've never worried about ownership before, how could that have been the secret ingredient all along, surely they'd have realised and incorporated it into their thinking?
Posted Jul 12, 2021 1:52 UTC (Mon)
by rodgerd (guest, #58896)
[Link]
When a practitioner has mastered solving problems in particular ways with particular tools, it can be almost impossible to unpick how they do things to relearn them another way - particularly because it's psychologically difficult to simply discard knowledge that may no longer be necessary. The more tinkering is required, the more attached one becomes to the thing, because one's expertise has become the work-arounds, rather than solving the nominal problem.
I've had a similar visceral skepticism to (for example) moving away from sysv init, or challenges to "everything's a text" ways of thinking. If you've adjusted your mental model to think that parsing the different output of *ix tools way of describing the same thing is normal and natural and good, a competing model is heresy!
Expertise can be a trap, because sometimes it's misplaced.
Posted Jul 12, 2021 3:28 UTC (Mon)
by ncm (guest, #165)
[Link] (45 responses)
Ownership is something everybody who deals with pointers learns to understand and deal with very early; the notion that anybody experienced does not is beyond absurd. If you already thought about ownership in precisely Rust's way, you will have found its convention easy to adopt. But there are other ways that also work, sometimes better than is permitted in Rust.
Not having the problems that Rust prevents makes time the Rust compiler takes, to gnaw its slow, ponderous way through your code, into time wasted.
Posted Jul 12, 2021 7:39 UTC (Mon)
by Sesse (subscriber, #53779)
[Link] (4 responses)
Posted Jul 12, 2021 9:40 UTC (Mon)
by khim (subscriber, #9252)
[Link] (2 responses)
It goes back to Linus saying (old one, predates Rust): Which is version of that one from The Mythical Man Month (that one is half-century old, as you know): Also: most ideas which Rust enforces mirror what so-called “modern C++” (with owners and ownership passing) and, surprisingly enough, “modern kernel C” proposes with it's sparse tool, ownership trees and other such things. Also: very small, very limited “fuzzing with coverage” finds so many violations of these rules in most large codebases (like Chromium or Linux kernel) that Linux kernel developers just started “la-la-la don't see anything” game (till people started using them to create actual working exploits at which point they have started taking notice). IOW with Rust I had the exact opposite experience from what @rodgerd talks about: I already was familiar with concepts and their importance… just hated Rust syntax (still do, BTW). And this makes me wonder about what exactly are you doing with C and C++ that “concept of ownership was entirely new to you”… and how you ensure that proper cleanup is done in case of failures.
Posted Jul 12, 2021 10:54 UTC (Mon)
by Sesse (subscriber, #53779)
[Link]
Posted Jul 18, 2021 18:44 UTC (Sun)
by marcH (subscriber, #57642)
[Link]
I think this is because when you read a piece of code, the difficult and error-prone part is the "stateful" part. A child can understand a one page-long function as long it's "pure" with only explicit inputs and outputs and no side-effect; it's "just maths"!
But good luck when that one page-long is updating some complex data structure that persists before and after the code is run. Then you need to keep in your head a lot more context and information that is not directly visible on the screen. If on top of that you also need to remember what other pieces code could be performing on the same data _at the same time_ then it's game over for most people and even for the smartest is much more cognitive strain. See excors's comment about locks etc. at https://lwn.net/Articles/862591/
This is also why writing hardware code is IMHO "harder" than software: hardware design is all about managing a huge number of concurrent states.
It's about time low-level software takes some elevation and safe (!) distance from hardware design and stops mimicking it because unlike hardware, software has no reason to burden developers with so much micro-management of data states and side effects. Instead, tell the compiler or static analyser who owns or borrows what data and when and let it find the bugs. Yes, that makes compilation much longer but that time is nothing compared to error-prone code reviews that often don't happen because no time has been planned for them. Let the machines do the tedious work, they're so much better at it.
Posted Jul 14, 2021 6:23 UTC (Wed)
by ncm (guest, #165)
[Link]
This is a transparently false statement.
Either you routinely leaked, double-freed, and/or used memory after freeing it, OR you (or somebody directing you) exercised a rigorous ownership policy. It is not surprising that you have forgotten all about it all since moving to more automated tooling.
Posted Jul 12, 2021 10:02 UTC (Mon)
by tialaramex (subscriber, #21167)
[Link] (39 responses)
Stroustrup's long tedious book has no section about ownership. Its awful - presumably machine-generated - index lacks entries for ownership, owning, or related words. Stroustrup's periodic rants about style or philosophy of programming did not seem to (in the time I had to re-read them for this comment) mention the idea. It was, I will thus claim, "Not top of mind". Multiple inheritance was important to Stroustrup. Overloading the pointer reference operator was important. Ownership? Never warrants a mention.
Years later the eventual standard C++ has a smart pointer, auto_ptr. But, auto_ptr doesn't reflect the idea of ownership. If anything it muddies the water, a program with auto_ptrs is confusing, or worse, confused, about what is owned and who by.
So in 2011 (ten years ago) C++ gets unique_ptr, shared_ptr, weak_ptr and so on. These, at last, reflect ownership. The unique_ptr owns the thing to which it is a unique pointer, a weak_ptr explicitly doesn't own anything. But C++ 11 still also has and uses raw pointers, and by this point C++ programmers are also using a lot of references, some of them believe references imply ownership, others the exact opposite. Worse, programs are entitled in C++ to just extract the raw pointer from all these smart pointers except weak_ptr (where for obvious reasons there might sometimes not be a raw pointer) and they do.
Only when you look at work proposed for future C++ do you see the sort of enlightenment about Ownership that Rust has.
And that's only half the story. I titled my comment "Ownership and lifetimes". While C++ grew some ideas about Ownership ten years ago, it still doesn't have Lifetimes. Bjarne has proposals, with straw man implementations for the basic idea, but I'd be astonished if (modulo ongoing concerns that Rust is eating their lunch) his feature makes it into C++ 23 because C++ is today a very Conservative programming language, much more ready to find reasons to do nothing than to change and grow.
Finally, you are concerned that Rust compilation is too slow. One of the reasons C++ in particular is able to be fast is a reckless, almost outright negligent approach to basic engineering. The One Definition Rule. To enable the compiler to process as many translation units as it likes, simultaneously and without communicating, C++ has a rule which says that a C++ program must only have One Definition for symbols visible from multiple translation units. This doesn't feel so bad right? Surely you will get an error from the compiler if you violate this rule? Nope. The compiler can't be sure it will ever notice, and if it doesn't the resulting binary is nonsense. That's why the ODR exists. As a result, C++ has "False positives for the question: Is This A Program?". This is clearly awful software engineering. But it _is_ faster for whatever that was worth.
Posted Jul 12, 2021 15:30 UTC (Mon)
by khim (subscriber, #9252)
[Link] (13 responses)
I think ownership is similar to zero and negative numbers. Romans have built very powerful and engineering-savvy (by ancient standards) civilization. Without knowing zero. It took centuries for it to travel from India to the West. Negative numbers took even longer. Yet today these are something we learn in school or even preschool. Similar, but more recent, example: two's complement numbers. They were used by EDSAC ¾ century ago. Today they are “no brainer”, everyone uses them. Yet when in 1989 C standard was written they were not established enough for the C standard to define signed overflow result! It was declared an “undefined behavior” and it was opinions about them started that long feud between C compiler developers and C developers… In the other post ncm talks about backward compatibility as the reason why Rust feels much safer than C or C++. But what kind of backward compatibility can be affected if you just say “it's no longer an undefined behavior to add MAX_INT and 1”? On the contrary: the whole reason switch from C/C++ to Rust is contemplated (although not guaranteed, obviously) is because C/C++ compilers treated it's users as “captive audience” for so long: while they retained “theoretical backward-compatibility” (as in: nonexistent code which never triggers obscure “undefined behaviors” which hardware and OS define precisely — should work after compiler update) they broke “practical backward-compatibility” (as in: code which was tested and ironed-out by decades of development was suddenly declared “non-compliant” and was broken). Rust was never intended as actual C++ replacement (although today a lot of people are actually contemplating if that may be possible or not) but it follows Linus rules to the backward compatibility: The rule is not "we don't break non-buggy user space" or "we don't break reasonable user-space". The rule is simply "we don't break user-space". Even if the breakage is totally incidental, that doesn't help the _user_. It's still breakage. With addon breaking user space is a bit like trees falling in the forest. If there's nobody around to see it, did it really break? Yes, before they can adopt such a stance they needed to spend almost a decade ironing out problems in the basics of Rust and making sure actual programs which are built against stable Rust features wouldn't contain too many crazy things. C and C++ always allowed crazy programs thus declaring exactly this principle is, probably, not possible. But complete and utter disregard to expectations and use of C and C++ by real programmers definitely triggered the crisis. Ultimately, after enough breakages, which were dismissed with “hey, standard allows us to do what we do, go fix your 20-years old code” it painted C/C++ compiler developers not as C/C++ developer friends, but as adversaries. The end result: after long, hard, really arduous process C/C++ compiler developers have finally brought C and C++ to the point where collection of artificial puzzles the language imposes on top of each programming task (all these “don't ever rely on two's complement arithmetic even if it's guaranteed by standard”, “never do arithmetic on Also: when you are doing “bad things” in C/C++ your punishment is often much harsher: instead of getting clear and simple compiler errors you observe how your program turns into pile of goo. But it's 100% truth: if Rust would ever replace C/C++ then it would be shared effort where achievements of C/C++ compiler developers at making C/C++ insanely, unaffordably, dangerous would be regarded as critical step.
Posted Jul 12, 2021 17:51 UTC (Mon)
by ncm (guest, #165)
[Link] (1 responses)
It is clever to add links to other postings, and wholly invent remarks to pretend to quote from them. It is even more clever to invent things that must surely have happened before he was born. But it is not clear why anyone should try to tease out the few, lonely correct statements that accidentally appear in the roiling cloud of phantasms presented.
Posted Jul 12, 2021 18:05 UTC (Mon)
by corbet (editor, #1)
[Link]
Thank you.
Posted Jul 12, 2021 18:52 UTC (Mon)
by JanC_ (guest, #34940)
[Link] (9 responses)
Posted Jul 13, 2021 10:34 UTC (Tue)
by khim (subscriber, #9252)
[Link] (8 responses)
This was logical back 1989. Today only two's complement representation is supported and all relevant CPUs handle overflow just fine. And if compiler developers worry about benchmarks then they can always use an appropriate switch to restore obsolete behavior. The problem with C/C++ is not certain peculiarities with misunderstandings about some complex corner-cases (it's inevitable when complex systems are involved) but absolute conviction of compiler developers in the fact that “mere users” don't even deserve a discussion. “It's my way or the highway” attitude permeates the discussion. Well… Rust looks like a nice place on the highway, so maybe it's time to pick 2nd option.
Posted Jul 13, 2021 20:51 UTC (Tue)
by mrugiero (guest, #153040)
[Link] (7 responses)
Posted Jul 14, 2021 9:24 UTC (Wed)
by khim (subscriber, #9252)
[Link] (6 responses)
If I understand correctly they wanted to support systems where signed overflow causes a trap without introducing a ways to intercept that trap. But declaring it “undefined behavior” they achieved that goal: system which don't cause overflow have no need to intercept that trap while systems which cause it are not valid C. Not sure how much sense that did even back in 1989, but in 2020 it doesn't make any sense at all: there are few CPUs which may still generate signals on overflow, but I don't know any where that's not optional. And now, when two's complement is mandatory, workaround is, actually, “very simple”. Instead of And yes, of course you stuff that into a simple templated function. Except since there are no standard modules, no central repo and so on… everyone who wants/needs it would probably need to implement it separately. Which would, most likely, lead to bugs like an uncaught attempts to use that function to check overflow of And these guys are talking about artificial puzzles the language imposes on top of each programming task? Really?
Posted Jul 15, 2021 2:16 UTC (Thu)
by mrugiero (guest, #153040)
[Link] (5 responses)
But yeah, talking about artificial puzzles when you have all those kinds of behavior and you don't even mandate warnings when you hit them is a bit hypocritical.
Posted Jul 15, 2021 14:12 UTC (Thu)
by mathstuf (subscriber, #69389)
[Link] (3 responses)
I may be misremembering, but didn't some platforms not provide a way to tell? That would require the compiler to emit manual checks for every possible overflowing operation in order to guarantee *a* behavior. This would also mean that optimizing to, e.g., FMA instructions is basically impossible because if the intermediate ops overflow, that needs to be handled as well.
But if there are no platforms that lack an "overflow happened" flag…meh.
Posted Jul 15, 2021 15:56 UTC (Thu)
by matthias (subscriber, #94967)
[Link]
If you do not know which platform your code will run on, you can still be not sure what the result of x+100 is in case of overflow, but you can at least be sure what (x+100)&0 is. And you can be sure that if you do an assertion x+100>x to test for an overflow that the assertion is not optimized away. Ok, it can be optimized away of the compiler can prove that there is not overflow, but this is no problem.
Posted Jul 15, 2021 16:26 UTC (Thu)
by farnz (subscriber, #17727)
[Link] (1 responses)
If such platforms exist and need to be cared about (like you, I'm not sure if they did, or did not), an easy solution would be to make the behaviour of signed integer overflow unspecified, rather than undefined.
In the C language spec, there are four groups of behaviour (from strongest definition to weakest):
If signed integer overflow became unspecified, such that the result of a signed integer overflow could be any integer value, then we're in a much better place. The compiler can just use the machine instruction (assuming it doesn't trap), and we don't have the pain where the compiler goes "well, if this is signed overflow, then behaviour is undefined, ergo I can assume it's not, ergo I can optimise out a check"; instead, signed overflow has to produce an integer, but which integer is not known by the code author.
Posted Jul 15, 2021 16:42 UTC (Thu)
by khim (subscriber, #9252)
[Link]
This is all well and good, but this requires some form of consensus. And in C/C++ world discussions are just not happening. Why have the coveted “pointer provenance” proposals were not incorporated into standard in 15 years (and counting)? Because not even C/C++ compiler writers can agree with each other. -std=friendly-c proposal had the exact some fate. And when someone tries to resolve the issue by “starting from scratch”… the end result is D, Java, C# or Rust… never a “friendly C”… Rust is just the first such “new C” language which is low-level enough to actually be usable for all kinds of usages. Starting from lowest-level just above assembler.
Posted Jul 20, 2021 10:25 UTC (Tue)
by anton (subscriber, #25547)
[Link]
Posted Jul 20, 2021 9:58 UTC (Tue)
by anton (subscriber, #25547)
[Link]
Posted Jul 13, 2021 16:15 UTC (Tue)
by plugwash (subscriber, #29694)
[Link] (9 responses)
To actually use the object owned by a C++ smart pointer, you are pretty much forced to explicitly (e.g. "get") or implicitly (e.g. operator* or operator->) extract a raw pointer or reference.
Which ties into your point about lifetimes, once that raw pointer or reference is extracted (again either explicitly or implicitly) there is nothing to ensure that the smart pointer outlives the raw pointer or reference that was extracted from it.
Posted Jul 13, 2021 20:56 UTC (Tue)
by mrugiero (guest, #153040)
[Link] (8 responses)
Posted Jul 13, 2021 23:16 UTC (Tue)
by roc (subscriber, #30627)
[Link] (7 responses)
Posted Jul 13, 2021 23:18 UTC (Tue)
by roc (subscriber, #30627)
[Link] (5 responses)
Posted Jul 14, 2021 9:32 UTC (Wed)
by khim (subscriber, #9252)
[Link] (4 responses)
Posted Jul 14, 2021 12:06 UTC (Wed)
by mathstuf (subscriber, #69389)
[Link] (3 responses)
- destroy
Rust can end `p`'s lifetime at the `foo` call, but C++ has lifetimes go to the end of the scope no matter what because there's no mechanism to say "`p` no longer exists and is now `foo`'s problem".
Posted Jul 14, 2021 12:44 UTC (Wed)
by khim (subscriber, #9252)
[Link] (2 responses)
True, sorry I wasn't clear. Object owned by It's actually even worse than that: Clang have attribute which solves that issue, but libstdc++ doesn't use it (and I'm not even sure if libc++ actually uses that attribute already). That's why I know some companies which demand that As I have said: “modern C++” is just a huge collection of footguns of different sizes and shapes thus it sounds really funny when it's proponent start talking about artificial puzzles the language imposes on top of each programming task. At least these rules exist in some kind of central location and compiler actually verifies them! Much better than when their violation turns your program into pile of goo without any complains from the compiler.
Posted Jul 14, 2021 13:04 UTC (Wed)
by mathstuf (subscriber, #69389)
[Link] (1 responses)
I tend to just use "Plain text" here because HTML is a pain to write by hand and seem to wish that Markdown of some kind worked :) . That said, I cannot guarantee that it isn't a footgun you weren't aware of ;) .
> std::unique_ptr can not be passed in register.
True. There's discussion about breaking the ABI for this (and other things), but ABI breaking is a *huge* topic and you have the monorepo-using "we rebuild everything ourselves so what even is ABI stability" on one side and the "we have critical libraries we cannot recompile anymore because we lost the source 10 years ago" on the other. I have no idea if that discussion will go anywhere other than heat generation, but I hope something *useful* comes out of it at least even if it is just "let's design in some escape hatches for ourselves in the future".
Posted Jul 17, 2021 16:06 UTC (Sat)
by mrugiero (guest, #153040)
[Link]
Posted Jul 15, 2021 2:10 UTC (Thu)
by mrugiero (guest, #153040)
[Link]
Posted Jul 14, 2021 7:28 UTC (Wed)
by ncm (guest, #165)
[Link] (14 responses)
Stroustrup never, ever rants. When he writes, every word counts. To perceive ranting only demonstrates confusion.
Your confusion about the One Definition Rule is revealing. In fact, the only requirement to spend an entire career coding C++ never once troubled by violations of the ODR is simply to obtain declarations via headers, rather than copy-pasting them. C has always been subject to undetected failures resulting from identical malpractice; what is new is that C++ gives the rule a formal name. C's linkage model, inherited by C++ for backward compatibility, brought with it C's weaknesses, but C++ now provides module support..
To believe that object ownership and lifetimes have not always been understood and actively managed as a matter of course by all C++ (and C) coders demonstrates further confusion.
To believe that C++ is not rapidly growing and changing is to believe a fantasy that C++20 is hardly different from C++17, from C++14, from C++11, from C++03, from C++98. In fact no language defined by a Standard has evolved more over that time, and few of the rest have. As more people adopt C++ anew in any given week than have ever heard of Rust, there is no worry about lunch. "There are only two kinds of languages: the ones people complain about, and the ones nobody uses." When we see more complaints about Rust than marveling over its ability to do what has been done before, it will merit more attention.
Writing fantastic falsehoods about a language and its users reveals nothing about them, but much about the writer.
Posted Jul 14, 2021 8:25 UTC (Wed)
by Cyberax (✭ supporter ✭, #52523)
[Link] (1 responses)
That's incorrect. You can get ODR violations by having multiple clashing template specializations, that can be in different parts of the application (for the added fun).
Back in MSVS 2006 days, I also spent several days debugging an issue where two inline functions (exempt from ODR) had different padding due to different compilation options.
Posted Jul 14, 2021 9:40 UTC (Wed)
by khim (subscriber, #9252)
[Link]
No need for even that. Compile your header once with Have fun. This story just never dies.
Posted Jul 14, 2021 8:33 UTC (Wed)
by Cyberax (✭ supporter ✭, #52523)
[Link] (3 responses)
Honestly, C++ is not growing rapidly. C++20 improvements were mostly in libraries and infrastructure around them.
The major language-level feature in C++20 are concepts, but they had to be cut down quite a bit. Coroutine support is at the barest minimum possible. Modules haven't made it either.
Posted Jul 15, 2021 5:52 UTC (Thu)
by ncm (guest, #165)
[Link] (2 responses)
Posted Jul 15, 2021 13:43 UTC (Thu)
by mathstuf (subscriber, #69389)
[Link]
Posted Jul 18, 2021 13:13 UTC (Sun)
by flussence (guest, #85566)
[Link]
Posted Jul 14, 2021 9:28 UTC (Wed)
by jezuch (subscriber, #52988)
[Link] (1 responses)
Newer compilers actually warn about ODR violations. Previously is was completely silent, so nobody knew how much they were violating it. I once compiled some random C++ program with such a compiler and I just sat and watched as the ODR violation warnings just scrolled by, on and on, endlessly.
So. "The difference between practice and theory is bigger in practice than in theory." (I think it's an old engineering saying.)
Posted Jul 14, 2021 12:26 UTC (Wed)
by tialaramex (subscriber, #21167)
[Link]
Posted Jul 14, 2021 15:20 UTC (Wed)
by tialaramex (subscriber, #21167)
[Link]
So, the Fact is that Stroustrup doesn't talk about ownership in that book. The much later (less than ten years old as I write this) Fourth Edition does talk extensively about ownership. Can't shut up about it actually. In some places, nearby text survives, or has been transplanted from earlier editions, but the talk of ownership is new. Why? Because of shared_ptr and unique_ptr which did not exist when the Second Edition was written. This inspires Stroustrup to explain even before introducing any C++ syntax at all, that programmers must "represent ... Ownership relationships" in their code. Which, in C++ 11 they now can try to do.
Throughout the Fourth Edition's "Tour" of C++, there are opportunities, taken or at least pointed out to the reader for their own benefit, to express Ownership. The Desk Calculator example explicitly tracks whether it owns the Input Stream it is using, offering methods to give it a reference (which it doesn't own) or a raw pointer (which it does) and clean up appropriately. Second Edition makes no mention of this, it too has a Desk Calculator example, but it doesn't bother with Input Streams, it just reaches inside the system's standard input and mangles that directly.
So, you're wrong, factually even if you firmly believe now that ownership has "always been understood and actively managed" it seems to have escaped mention by the language's father in their text intended to introduce the language _until_ decades later the language grew facilities to actually try to "actively manage" this problem.
Lifetimes are an interesting contrast. The Second Edition does spend a few paragraphs on this topic, but mostly the reader is encouraged not to think about it too hard. Objects begin living when they're Constructed, and this lifetime ends when their name goes out of scope whereupon they will be Destroyed. Except, that's not quite true and there are a few paragraphs trying to justify that before a reference to paragraphs later in the book about the Free Store.
By the Fourth Edition, lifetimes get their own sizeable Chapter in the much larger book, "Construction, Cleanup, Copy and Move". So I agree that by this point Stroustrup is aware that this is a problem, indeed that chapter uses words like "trap" and "mistake" and most sub-sections are accompanied by considerable verbiage basically encouraging the programmer to try harder not to get this wrong. But he doesn't have anything to really propose as a solution. Yet.
Today, Stroustrup has a proposed change to C++ to add actual lifetime tracking. I don't expect it will make it into C++ 23. You claim that Stroustrup "never, ever rants" but he seems quite animated when it comes to the committee not accepting all his ideas as quickly as he might like or adjusting them in ways he isn't pleased with, and equally animated about cutting down other people's proposals.
Posted Jul 14, 2021 15:37 UTC (Wed)
by mathstuf (subscriber, #69389)
[Link] (2 responses)
Just had to deal with this last week in fact. ODR violations are rampant when some dependency does not follow C++ "best practices" nor uses extra-language bits that are compiler/platform dependent (`__attribute__(visibility)` and `__declspec`).
One of our dependencies doesn't export symbols properly. Luckily, CMake has a way to say "le sigh, just export everything" on Windows to fix that problem. However, there's a deeper problem lurking in such a codebase: RTTI duplication. There are these simple interface classes that have their definitions completely in the header. Seems reasonable; header-only is fine, right? However, the source code of the library does `typeid` comparisons with a `std::type_info` that is passed in to see which of these interface types you're asking about. Because the complete definition (trivial `{}` bodies for the ctor and dtor with pure virtual methods otherwise) are in the header, every library that sees this declaration can say "huh, no export symbol? I can make it mine" and generate its own unique RTTI table for the type. The linker will resolve these together into a shared library boundary, but nothing resolves it across a shared library boundary at runtime, so you then get two different `typeid` blocks for the same type. Hilarity ensues when the dependency goes "yeah, I have no idea what you're on about, have a `nullptr`".
Now, C++ *could* have just standardized `export` or some other syntax to say whether a declaration "belongs" to the current TU's target or not, but that requires macros at compile time to know which library you're actually compiling for so things get linked up properly. And C++ *hates* talking about compilation strategies beyond "pile of TU outputs linked into a program" because it never bothered to reign in the madness from the beginning and now suggesting anything is likely to get somebody clutching pearls immediately. So, C++20 has modules where what is "in" and what is "out" can be more easily tracked with actual standard-compliant syntax, but this has been an issue since C's textual inclusion compilation model was a thing and only now we have a solution that requires rewriting all of your headers and leaving old, dumb `%.o: %.cpp` build systems behind anyways (because compiling C++20 modules is the same problem as compiling Fortran modules and everyone remembers how much fun that has been, right?).
Posted Jul 15, 2021 11:49 UTC (Thu)
by foom (subscriber, #14868)
[Link] (1 responses)
Neither MacOS (mach-o) nor Linux (elf) shared libraries have this issue. They both work fine in this situation, by ensuring that vague linkage symbols are exported and deduplicated across the shared library boundaries by default.
One might expect this would have been addressed in Windows at some point in the last few decades...but it hasn't been.
Posted Jul 15, 2021 13:46 UTC (Thu)
by mathstuf (subscriber, #69389)
[Link]
Posted Jul 18, 2021 18:10 UTC (Sun)
by marcH (subscriber, #57642)
[Link]
OK, so language theory discussions are fascinating (thank you everyone) but now let's get real a bit. Let's pretend I'm managing a software team and I've been asked to get rid of most safety issues in the C++ codebase of our mission-critical application. What -Wold-unsafe-c++ compiler flag do I turn on to make sure my not-all-perfect developers don't make any mistake and stick to "modern C++"? If that compiler flag is not available, what static analyzer(s) should I use? I have practically unlimited budget so I don't mind commercial products. (Even an unlimited budget is unfortunately not enough to hire an elite team of C++ developers who never make any mistake)
> there is no worry about lunch.
I don't think anyone doubts C++ will still make a lot of money after even the youngest people in this discussion are dead, no reason to feel threatened.
Posted Jul 18, 2021 18:49 UTC (Sun)
by marcH (subscriber, #57642)
[Link]
Whether it's true or not, I'm afraid you don't realize how cultish that is perceived.
Posted Jul 12, 2021 4:03 UTC (Mon)
by roc (subscriber, #30627)
[Link]
Posted Jul 13, 2021 10:02 UTC (Tue)
by ofranja (guest, #11084)
[Link]
This could be said about the mere act of programming, specially in a compiled language, and specially in C++.
It causes me surprise to hear this statement from someone with so much baggage in C++, one would expect the years of experience to have shown humans are very prone to mistakes, even by oneself. And in the event that the software one writes is perfect, there is still the need to deal with software from less-than-perfect human beings. Turning a certain class of mistakes into compile-time errors sounds like a good trade off for the majority of situations where the software itself is of any importance.
> This quirk of human psychology may be seen in many other activities, wherever unnecessary difficulty is treated as a virtue. The fanaticism that tends to result is the same in all cases.
I would not discard this effect, but again: same points apply to C++. I've heard similar argument (fanatismus) being used for C++ as well, by the way.
Unless we apply an unbiased reasoning to this argument I would say it is very selective of the borrow checker. It may be a learning curve in itself but just because C++ does not provide one it doesn't make it magically easier. I would argue the opposite.
Posted Jul 12, 2021 10:20 UTC (Mon)
by professor (subscriber, #63325)
[Link] (4 responses)
Is Rust Java 2.0?
Posted Jul 12, 2021 11:08 UTC (Mon)
by excors (subscriber, #95769)
[Link] (3 responses)
What happened is that people discovered it's nearly impossible to do that correctly in non-trivial programs. You can design your code with a rule that a certain set of variables is only accessed by threads holding a certain lock, which would guarantee it's safe from race conditions; but the old languages don't provide any tools to enforce that, and in practice it's very easy to make mistakes and access the variable without the lock. Or in attempting to avoid race conditions you might add too many locks, or try to acquire one recursively, and then you deadlock.
I think what happened next is that people started recognising the value of more modern (i.e. 1980s instead of 1960s) concurrency design patterns, with multiple single-threaded modules linked by thread-safe message queues. You can implement that in C, but it's very easy in a large program to make a mistake and become non-thread-safe again without noticing (e.g. by accessing a global variable, or by sharing a mutable data structure over the message queue, possibly via some third-party library that you can't redesign to your new style of concurrency).
Then people started designing languages that make the good patterns much easier to use, and make the bad patterns harder or impossible, so you can be much more confident that any code written in that language and accepted by the compiler is concurrency-safe.
Posted Jul 13, 2021 10:55 UTC (Tue)
by khim (subscriber, #9252)
[Link] (2 responses)
That's not Rust, that's Java 1.0. Rust picked many things from many languages (it's definitely not the first “safe language”), but it did three things which made it unique: This, basically, put it into unique position for now. (2) is especially important: suddenly, you can have amenities of high-level language while doing very low-level stuff. Like kernel or even embedded firmware. C#, Java, or Haskell (while certainly nice languages for some uses) would never be able to replace C/C++. Simply because they need fat runtime. Even older versions of Ada couldn't do safe dynamic memory processing without fat runtime (not 100% sure if modern versions can do that). With Rust you can do all kinds of projects which previously forced you to use C or C++. Doesn't necessarily mean you would want to do that. But you can. This changes things and makes Rust, most definitely, something different that Java 2.0.
Posted Jul 13, 2021 21:05 UTC (Tue)
by mrugiero (guest, #153040)
[Link] (1 responses)
Posted Jul 18, 2021 17:54 UTC (Sun)
by marcH (subscriber, #57642)
[Link]
I would call this "engineering".
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
ncm
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
C++ can express abstractions that Rust (still) cannot.
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Python with pip, Node.js with NPM, Rust with Cargo...
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Especially that there were already some backdoored packages found in, for example, NPM and PyPI, so the threat is very real.
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
> The pleasure comes from the feeling of winning on the compiler/debugger/runtime, not from the language itself.
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
unsafe
.Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
In that case: good for you!
Or, nobody has tried to break your code yet.
But either way, most of us are not so gifted/lucky and are very greatful indeed for any help the compiler can give us.
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
> In my case, in the past decade I have spent strictly more time preparing compiler bug reports than in debugging memory usage errors.
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
But is this because your code does not have memory usage errors, or because you just don't know about them (perhaps they are hard to trigger in normal usage)?
Do you know?
*How* can you be sure?
need to “fight” it for real… 9 times out of 10 it's cases where compiler discovers some hidden problem in your design! Which would stay as source of hidden bugs for months or maybe years in most other languages!
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
> On the other hand, in my experience these bugs are 95% on (not tested) error handler code paths or simply unused/unreachable code, because most of the real bugs are found by tests.
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
panic!
… but that's still a step forward from what happens in C/C++ programs.Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Yes, religion is a more appropriate analogue for the attachment that programmers feel to programming languages than Stockholm syndrome. Another phenomenon that may explain the attachment is choice-supportive bias (and that may also be related to religion).
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Ransomware? Social engineering and MS Windows treating "opening documents" the same as "executing a binary" (along with awful UI design so the user genuinely can't tell the difference most of the time), not memory corruption.
SQL injection & XSS vulnerabilities? Improper validating/escaping inputs, not memory corruption
SSH/SSL downgrade attacks? Protocol misdesign, not memory corruption
IoT botnets? Hard-coded passwords/backdoors, not memory corruption
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
- VIDEO: https://www.youtube.com/watch?v=PjbGojjnBZQ
- SLIDES: https://github.com/Microsoft/MSRC-Security-Research/blob/...
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
https://msrc-blog.microsoft.com/2019/07/16/a-proactive-approach-to-more-secure-code/
(repeats same graph and statistic, introduces the case for Rust in that context)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
(Windows 7 is still running on at least 100 million PCs)
> The satisfaction one gains in having solved the artificial puzzles the language imposes on top of each programming task… has nothing do with Rust.
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
unsafe
.Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
https://en.wikipedia.org/wiki/Referential_transparency
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
nullptr
”, “never pass null
as reference”, “never use std::move
to return value from function… but don't forget it in other places”… and so on) for C++ have become larger than what Rust demands.Ownership and lifetimes
...perhaps the lifetime of this particular thread has run its course. I don't see anything good coming from this kind of discussion; let's stop here.
Speaking of lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
if (x + 100 < x)
you just have to write something like the following: if (std::std::is_signed_v<decltype(x)> ? static_cast<decltype(x)>(static_cast<std::make_unsigned_t<decltype(x)>>)(x) + 100U) < x : x + 100U < x)
float
or double
… or it would be miscomplied because someone used -std=c++20 instead of proper -std=gnu++20… as one of members of pilot Rust program for Android said: I don't feel as satisfied when reviewing Rust code: I always find something to comment on in C++ changes, usually some kind of C++ quirk or a memory management problem. When reviewing Rust changes it makes me uncomfortable if I don't find anything to comment on ("Am I being thorough?").Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Concerning integer overflow, the result with a given bit-width is the same for twos-complement arithmetic across all hardware (they all can perform modulo arithmetic, aka wrapping on overflow). And all architectures introduced since 1970 use twos-complement arithmetic exclusively (while, e.g., . So for integer overflow, the behaviour could be just standarized, no need for cop-outs like implementation-defined or implementation-specified. As an example modulo arithmetic has been standardized in Java from the start.
Ownership and lifetimes
Backwards-compatible C compilers in the relaxed Linus Torvals sense (if no one notices, it's not breakage) are possible and desirable.
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
int& pp = *p;
p = nullptr;
cout << pp;
Ownership and lifetimes
You forgot to add Ownership and lifetimes
std::move
to the mix. The fact that after calling foo(std::move(p));
you can have so many possibilities (p
may be destroyed or not destroyed, usable or unusable, etc… and compiler wouldn't ever reveal to you what happened… static analyzers sometimes might) opens up another nice room with so many nice footguns of different sizes…
Ownership and lifetimes
- copy
- assign into (via move or copy)
- call methods on (though many APIs suck and don't provide ways of saying "this makes no sense anymore")
> In that example, `p` cannot be destroyed.
Ownership and lifetimes
p
may be destroyed or not destroyed, p
may be usable or unusable, etc (I hope `p` just means p
in your message and not some new footgun C++ gained while I wasn't looking).p
have a destructor (and said destructor is, of course, the whole reason std::unique_ptr
exists) thus it's non-trivial for the purposes of call which, in turn, means, that destruction of the object pointed by p
still happens in the caller, not callee and, maybe even more importantly, std::unique_ptr
can not be passed in register.std::unique_ptr
shouldn't be used to pass arguments to function and return results from it. Instead you have to use owner<T>
syntax and turn it into std::unique_ptr
in your function and back when you call anything.Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
> You can get ODR violations by having multiple clashing template specializations, that can be in different parts of the application (for the added fun).
Ownership and lifetimes
-mavx512f
and once without it and you may have nice and long investigation where you would try to understand why minor changes to totally unrelated code creates something that explodes on Ryzen cluster and where clean build either always produces working binaries but incremental builds sometimes lead to crashes.Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
Ownership and lifetimes
There is a shortage of developers, not a shortage of code to write. Apparently you can still get paid to fix COBOL code:
https://www.theverge.com/2020/4/14/21219561/coronavirus-p...
Ownership and lifetimes
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
> Then people started designing languages that make the good patterns much easier to use, and make the bad patterns harder or impossible
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)
Announcing Arti, a pure-Rust Tor implementation (Tor blog)