The snark is really offputting. As a user of software trying to protect my personal information in an increasingly threatening world, I would like memory safety in more places. From the outside looking in it feels like there are a lot of C and C++ programmers trying to deflect from how poorly their attempts at memory safety have gone and just snarking at the people trying to fix it.
I will say that the point about FFI boundaries needing more care is a valid one I had not considered.
Is there a name for people who don't work in C++ and never, ever hear "rewrite it in Rust" except from programmers who seem like they're a bit hurt that their language has a competitor?
At a point, one surgeon discovered that, with incontestable proof, and started advocating for hand-washing. When almost all surgeons noticed that something so simple and easy had that much impact, they immediately adopted it and saved a lot of suffering from their patients...
Ok, of course they didn't. Almost everybody fought the few wand-washing advocates, tried to shame and discredit them, tried to generate bullshit evidence saying that it didn't help, tried to convince everybody that it didn't matter, all the time denying to themselves that it worked. Surgeons only started washing unanimously their hands when that generation of them retired.
> Despite various publications of results where hand-washing reduced mortality to below 2%, Semmelweis's observations conflicted with the established scientific and medical opinions of the time and his ideas were rejected by the medical community. He could offer no theoretical explanation for his findings of reduced mortality due to hand-washing, and some doctors were offended at the suggestion that they should wash their hands and mocked him for it. In 1865, the increasingly outspoken Semmelweis allegedly suffered a nervous breakdown and was committed to an asylum by his colleagues. [...] His findings earned widespread acceptance only years after his death, when Louis Pasteur confirmed the germ theory, giving Semmelweis' observations a theoretical explanation, and Joseph Lister, acting on Pasteur's research, practised and operated using hygienic methods, with great success.
Those that fought against a new idea are colleges of those that fought for an idea and colleges of those that are ambivalent to the idea
Like the strawman argument where it creates a flawed argument to declare victory over, the strawman attack picks out the most extreme case to declare victory over
You'll see this done on left/right political debates where instead of recognizing that most people can find agreeable common ground on 80% of issues, extreme advocates will pick out the opposing extreme advocates to vilify each others' groups
I think a lot of Rust programmers appreciate where C is the right tool, & a lot of C programmers can admit that memory errors are a pain in the ass
That's true, however IME C++ programmers are the most in denial. And yes, RAII has helped a lot. But still we see way too many memory errors coming from C++ code bases.
The first F in FFI [1] means that you have an interaction of non-Rust and Rust. (Or two non-Rust languages but that's moot).
[1]: https://en.wikipedia.org/wiki/Foreign_function_interface
FFI is inherently unsafe regardless of language.
The standard library and ecosystem follow the philosophy of making it easy to do the right thing, and preferably impossible to do the wrong thing; made possible by a very expressive type system. Of course logic bugs are possible in any language, and I wouldn't suggest rewriting a battle-tested Java application in Rust. But if you are already committed to rewriting (or starting a greenfield project) then Rust is a good choice for more reasons than just memory safety.
IMHO the big advantage here is not languages that are memory safe, so much as catch many of those misunderstandings at compile time, or runtime in a better way than "Crash" or worse - silent corruption (Much of this is available for c++ in static analysis and sanitizers and other runtime checks, but them being non-default options seems to make them oddly ignored). I feel them being non-default also means there isn't the same focus on performance - and it's an everything-debug-and-slow-as-hell with a UX purely designed around the dev debugging their own code, or no-checking-at-all-no-brakes rather than in between.
The classic "memory errors" that are purely local and just not checking the bounds of an array or similar have been pretty much eliminated if you actually use the features (containers, iterators etc.) of the c++ standard library.
While that's true, memory safety bugs are particularly dangerous from a security perspective.
Is it? Large companies with huge code bases (meaning lots of statistical significance) have said 70 percent of their CVEs could not have happened if that code was written in Rust. Not only are those kinds of errors very common, they can be IMHO a pain to debug.
So given the prevalence of memory safety issues, why do you find it important to remind people that other kinds of bugs exist?
Same thing with features. Fewer, better-vetted features mean more secure systems.
C encourages better, simpler designs, but at the cost of making it nearly impossible to write safe code. Garbage-collected languages remove entire classes of errors, but they make it easier to implement lazy, unnecessarily complex designs.
Maybe Rust offers the best of both worlds? (I say this mostly but not entirely tongue in cheek.)
I can count with my fingertips exactly zero times that has happened to me. The times my personal information was compromised happened to me is when I downloaded malicious software. Malicious software doesn't care whether your process has "memory safety", it'll extract anything from the processes memory it wishes. Sure memory safety would at least rule out simple binary exploits, but to be targeted by one is not exactly common.
I don't get the narrative of an ever increasingly threatening world. If anything things have only been getting better, at least for the individual users. I remember individuals getting pwned very frequently in the old days. Now, not so much, with DEP, ASLR, and stack canaries. The attacks on businesses have increased, but those attacks are mostly just phishing or otherwise human error related.
Attack vectors for actual PI retrieval rarely incorporate some obscure memory bug in a program allowing RCE or remote access. Unless you're a high value target.
It happens with some frequency. WhatsApp was exploited due to a memory safety vulnerability, for example. Chrome 0days for memory unsafe vulns are definitely exploited in the wild.
Typically companies have much much larger attack surface, so a technique like phishing is going to be far cheaper to execute. But even still, I've seen a memory safety vulnerability used in an attack against a company.
The thing is that most companies' attack surface isn't in C/C++ because... that would suck, so they don't do it. Or if they do they use a specific codebase that's been heavily invested in over decades and they sandbox and isolate the services.
So on the one hand, yes, most attacks on companies are not due to memory safety issues but that's in part because of the investments into memory safety.
> I remember individuals getting pwned very frequently in the old days.
Yep, significant efforts were made to make the internet a safer place. Primarily the sandboxing and disabling of third party plugins in browsers.
But this doesn't really matter. Yes, there are other issues like phishing and those are being addressed with other techniques. There are issues like sql injection and those are also being addressed. That doesn't mean that memory safety isn't an issue.
This talk is what convinced me to move to Rust definitely. The audience is the perfect representation of the C++ community.
I take issue with the notion that languages can have competitors. Languages are tools, and some languages are more suited to some tasks than others, but each has their place.
Saying languages compete with each other is a bit like saying different kinds of hammers compete with each other. They don't, each is just optimized for a different kind of hammering.
https://www.nytimes.com/wirecutter/reviews/best-hammer/
Your productivity in any language is directly proportional to your time and effort investment in it. It's in your best interests to pick the language that's likely to thrive and spend time learning its ins and outs. On the flip side, betting on a horse that doesn't win could mean the loss of months or years of effort. This is why people evangelize the platforms they're invested in - convincing other people to join improves the health of the platform, increasing their return on investment. More adoption => more libraries => more projects => more jobs => more adoption. A virtuous cycle.
This evangelizing can sometimes become contentious if others perceive it as an attack on their platform. People defend their language mostly because they don't want to see it lose popularity. If it did, their language's viability is threatened and their investment is in jeopardy.
It's also partly because they've spent so working with this language that it's become a part of their identity. A critique of the tool is perceived as an attack on the person.
Then someone invents a hammer. You no longer use a rock for driving nails.
Then someone invents a can opener. You no longer use a rock for opening cans.
Then someone invents gigantic sledge hammers. You no longer use a rock to smash things.
Languages definitely have competitors. Java took a lot of projects that would have otherwise been written in C++. Rust will take a lot of projects that would have otherwise been written in C or C++, but languages like Java weren't suitable for. Eventually, those two languages might be completely supplanted (but it would take a very long time). Because they lost to competitors in each space.
Just stop. Programming languages aren’t like carpentry tools at all.
For a programming language to survive you need a critical amount of users so that they can support each other by teaching each other, writing libraries, and influencing management in private and public industries to adopt the languages that are wanted. That naturally leads to at least some competetition for mindshare among developers, even (especially?) among the grassroots.
Just let this lame analogy die already.
Sure, but the way that rust does it comes with a heavy cost, including slow compile times and confusing lifetime semantics (and, in my subjective opinion, poor syntax). There are other techniques to achieve memory safety that are simpler and more ergonomic (to me at least) than borrow checking. Vale, for example, has some interesting ideas in this space.
One of the main reasons that people complain about rust is because it has an extremely loud group of evangelists who often shame other people for taking a different approach and essentially refuse to acknowledge that others have a point about its weaknesses. It is always off-putting when a community goes around telling everyone else that they are doing it wrong, particularly when they are ignoring the very real flaws in their own way of doing things.
Name them. Maybe you don’t have real names but you can probably cough up some Internet handles.
The “fanatical rustacean” is a bit of a 2018 thing. Out of the few who are actually evangelists I an fact think that they can be way too nice and conflict-averse (“right tool for the job”).
No one denies that there are "simpler ways to achieve memory safety". Stop-the-world garbage collection and reference-counting are exactly just that. Vale uses generational references, which, IIUC, has both memory and runtime costs when compared to the borrow checker.
* Rust does not have slow compile times compared to C++. They are often faster. Linking tends to be the slow part. C++ compilation speeds suffer heavily from header files. * Lifetime semantics can be confusing in any language. In C++, you ignore it, you get crashes from dangling pointers. In Rust, you ignore it, you don't get it compiling - which forces you to think about them. The lifetime concerns are always present unless you're using a garbage-collected/reference-counted language.
My experience is that the Rust community is very tolerant and patient. Can you cite an example where a "loud evangelist" shames other people? Also, in my experience, when that happens they are criticised by the same Rust community.
Stop parroting memes. "Rust evangelists strike force" and "Rewrite it in Rust" do not actually exist.
Point me at your local hobby or professional club and I bet my neck I'll find you at least one fanatic. But I will not use that to deride your hobby activity. So don't do that for Rust, please. The community is huge and doesn't subscribe under the fanaticism of a few loonies.
It's usually something like "I don't need the compiler holding my hand, I know what I'm doing", or "I'll just write in Go/Java/etc. so I don't have to worry about memory".
Whenever anyone that gives it a serious try to build something and does not become a convert that touts its greatness as the one and true go...language they are usually given the no true scotsman treatment or a variation of the emperor's new clothes.
The shouting fanbeings of rust put me off looking into it for years, because when I kept getting "rewrite it in rust!" as the answer to "there's a problem with $THIS_CODE" when talking with colleagues, _even when those colleagues had minimal rust experience_, all I could conclude was that the whole thing was an empty promise and that no-one knew how to solve the problem, but everyone "knew" that the New Cool Language was the way to fix everything.
Generalisation from incomplete data - no doubt there was a sensible majority in the rust community, but the fanbeings were _loud_.
FWIW I was wrong: I'm getting into rust now and I like what I see, and the discussions around it online and with colleagues are pretty sensible. But, it's taken a while to get there and when you've been in tech for a couple of decades you see this hype cycle and get jaded to it. Erlang is the new hotness ... OCaml is the new hotness ... Java is the new hotness, rewrite everything in Java, wait C# is the new hotness...
I suspect rust is here to stay, and I'm gonna learn more about it, and I regret some of my past words about it. But my problem was never with the increased memory safety, or the language at all, pretty much, just the early community.
TL;DR: Other humans are the worst, bug reported, fix unlikely :)
Let's not judge a whole tech stack by a few loud evangelists?
That tech stack has undeniable advantages. I'm focusing on that and don't pay attention to the emotional types.
And it's IMO a flawed judgement technique to distance oneself from technology because some randos struck you as fanatics.
Go and judge for yourself on the ground, experimenting. It's the only way to be sure.
Glad you're coming around btw.
C offers an appropriate level of memory safety for the problems it solves. I take managing my own memory over unreadable code and dependency hell any day. The fact that C will run on DSPs with 27-bit pointers is just an added bonus. The ease of manual memory management in C is an advantage, not a downside.
And the tooling for C will exist long after rust has been abandoned.
args.into_iter().skip(1).and_so_on...
Which I can't stand to look at, I'd vastly prefer something like... for arg in args[1..*] { ... }
Though my true preference would be S-expressions, but I realise that people lose their marbles when they see something like... (for-each do-something
(slice args #:from 1))
Of course Common Lisp is one of the faster slow-as-molasses languages, but having a language that uses S-expressions which can compile down into a small, fast binary would be the dream. Carp would be interesting if it didn't use Clojure syntax.With that in mind, what other lisps are running circles against common-lisp? I would love to give those languages a try. From my experience sbcl common-lisp is about equal to Go and Java which I consider pretty fast languages.
Its not as fast as C,C++,Rust but when I think of slow I think of Python, not lisps.
fn main() {
let args = vec![1, 2, 3, 4, 5];
for arg in &args[1..] {
println!("{arg}");
}
}I may be stating the obvious, but that's a bit of a strawman. Yes, writing good FFI code is hard; yes it could result in security/soundness issues; yes, we could use better tools in this space.
But nobody rewrites C code in Rust if they believe existing codebase is free of memory safety hazards; they rewrite it because they think the result will contain fewer hazards, even accounting for the potential problems at the FFI boundary.
If I could remove tens of thousands of lines of hard-to-analyze C code, and replace it with tens of thousands of lines of safe Rust, paired with a few hundred lines of hard-to-analyze FFI adapters, that sounds like a pretty good tradeoff to me. I now know exactly where to focus my attention, and I can have confidence that the situation will only improve with time: better tooling may allow me to improve the dangerous FFI layer, and in the meantime I can recklessly improve the safe Rust module without fear of introducing new memory unsafety bugs, unsound behavior, or data races.
I looked for the author's (whoever they are) proposed solution and it's this:
"This is because many of the FFI bugs surveyed are fundamentally cross-language issues. Instead, we propose that both C and Rust must conform to a shared, formally-based domain-specific language, which provides a safe-by-construction abstraction over the FFI boundary."
Such a thing is a "new thing", and isn't going to retroactively apply to the legacy C code written before this new thing... so how does that help?
(Full disclosure: I'm a professional programmer who has written in C, C++, C# for many years, and now I choose to write new things in Rust.)
Well some people are. Is there a word for attacking the weakest real version of an opposing argument? Strawman usually implies you are attacking a fake version of the argument, but on the internet you can usually find someone who actually holds an easily refuted point of view, just because of the law of large numbers...
Typically the Rust Evangelization Task Force does not include people who are actually going to maintain the software (or do the rewrite themselves).
I'd offer that even if the existing codebase is free of memory safety hazards, low confidence in future changes being able to keep it free of memory safety hazards in a cost efficient manner is a motivation to migrate.
The idea that a program can approach some optimal bug-free state, never to be modified or refactored again, doesn't resemble any project I've ever encountered.
What if someone makes a strawman out of memory problems so they can rewrite it in Rust ?
That's pretty generous. They re-write it in rust because a "Show HN: Thing-X, in Rust gets upvotes."
But seriously, that’s kinda silly to propose as a motive for a software project requiring significant effort.
for anything that is actively developed it's a whole other story, even if you are confident that the current codebase is safe, each added feature has a risk that it breaks some unwritten contract somewhere and introduces security issues.
eg. look at recent vulnerability in sudo, at and second sight it was safe and secure, triggering it required unobvious corner case.
how many of similar issues you could have in your codebase that could be dormant for years?
both of these projects are still actively developed.
* https://lists.gnu.org/archive/html/bash-announce/2022-09/msg...
* https://lists.gnu.org/archive/html/coreutils-announce/2023-0...
Not the best start already. Hmm, written by 'Anonymous Authors'. So is this supposed to be a joke? Is the implication that speaking out against rust makes you the target of unjust persecution? Is it written by people known to have an axe to grind?
Abstract: This is a free-verse poem setting up a rust advocate strawman. Do such people exist on the internet? Yes. But time cube also exists on the internet.
Introduction: Incrementally rewriting a C/C++ system in rust is bad because FFI can have problems. Which can be simplified with a different spin to be, "C/C++ are so bad that they contaminate even the mighty rust."
It feels like the paper might have some good information that would be useful to people who want to augment their codebases with rust. However, it starts off so combative, that I don't think I could seriously recommend anyone actually read it. Maybe just skip the abstract and intro and see what you get out of it?
I've written C and C++ for a long time, and... Alas, you're not completely wrong lol.
At work there's a still unresolved ticket of me trying to get cross compilation working, and it's basically impossible because of the way most C/C++ tooling is built and works. CLang kinda works nicer here than GCC, but there were other issues with that that I encountered. Basically, it's nearly unsolvable due to the number of hoops one needs to jump to properly link against a dynamic library produced for the target platform (if the host platform is different).
Those are totally irrelevant
No, I don't.
Follow up question then: Is it normal for pre-prints that go out for blind peer reviews to be publicly posted like this? Should I be interpreting this as a leak of some sort, someone trying to get their work public eyeballs without messing with the peer review, etc? Or is this just par for the course in academia?
1. C can make aggressive aliasing optimizations based on observable behavior, meaning that the compiler may or may not optimize `add_twice` based on the results of any alias analysis it chooses to do. In other words: the author's assumption that a C compiler won't optimize `add_twice` in the same way that Rust will is not a guarantee, and assuming that it won't is relying on unspecified behavior.
2. The author claims that the result of the optimization can result in memory unsafety, and it's not immediately clear to me that that's true: it might be true in an interprocedural sense due to another function's assumptions about how `add_twice` affects its parameters, but this is the same incorrect assumption as in (1). In that sense, Rust is not really involved at all here.
In C, objects of the same type are presumed to alias unless the compiler can prove otherwise. Unless add_twice is compiled as a file-scoped (i.e. static) function or with equivalent compiler-specific options, the compiler cannot prove that a and b don't alias, and therefore it cannot make such an optimization. This is why C99 added the "restrict" keyword, to tell the compiler that two parameters of the same type do not alias.
This is indirectly why type punning is problematic in C--because C is pessimistic about aliasing, at least for objects of the same type, you normally can't run afoul of aliasing issues as the compiler bares the burden of ensuring safe behavior. But for objects of different types, the compiler can assume they don't alias. When type punning, at the point of usage it appears to the programmer and the compiler that they might alias (because they're the same type), but at some point earlier or later when operating on the original type of the type-punned object(s), the compiler can assume lack of aliasing, generating optimizations that at a distance break the type-safe code. Which is precisely the issue here with Rust FFI, and almost for the same reason--conflict between different aliasing rules in different contexts as Rust presumes lack of aliasing even for objects of the same type. In effect, with Rust FFI you're always type punning arguments passed into a Rust function, which is absolutely a noteworthy issue that absolutely should be addressed by adjusting the default semantics Rust applies to extern'd functions. (C isn't the only language that could run afoul of such aggressive semantics for foreign functions.)
However, when you call such function from C, the C compiler has no clue about exclusive references, so obviously it can't enforce their invariants. The C side can provide aliased pointers and break Rust's assumptions. The Rust side assumes it's called correctly, and the C side can't know what is correct.
They identify FFI as an area where it is easy to make mistakes which lead to undefined behavior, argue that it is the result of competing assumptions between C and rust, and then propose a formal system implemented in both in C and in Rust to resolve those issues.
Sure, the problems they identify wouldn’t exist if everything was rewritten in Rust, but on a large enough / old enough codebase, that may impossible to do all at once. This paper attempts to find a way to make rust better within those organizational constraints.
The audience here is not engineering managers deciding in your next tech stack, but programming language researchers.
Yes. It looks like the paper has some useful things to say. However, that abstract + intro is like walking into a wedding and splashing red paint on the bride and then announcing a bunch of very good reasons why the wedding should not continue. Nobody is going to be listening.
Perhaps another way to say it is that nobody is misreading their intension. What they're doing is ignoring reasonable concerns said in an unreasonable way.
These discussions frequently feel “tense”, wherein criticisms based on specific technical details are tied to an attack on the language as a whole.
In that context, the authors definitely shot themselves in the foot with the opening/title.
Yes, FFI is dangerous, that's true regardless of language (including C). FFI is inherently platform, language, and even compiler specific. It's super tricky to get right.
Because no-one would want to put their name to this.
There may be some valid arguments in there (though it seems to be just "FFI is hard ergo noone should never attempt phased migration of any system codebase"), but even content aside, the tone is that of a moody teenager. The abstract in particular is one of the worst things I've ever read - the introduction doesn't read fantastically eitherr.
Hell, the first example isn't equivalent code. You can't share references across an FFI boundary with C.
Now, for personal software I say hell yes. If a person is writing code for the fun of it, or simply wants to learn about concepts, rewrite whatever in whatever and have a great time. That's the point! But for professional/commercial software, rewriting is probably more expensive than is realized.
- Modeling concurrency across boundaries, like if you're p using goroutines but also tokio, how the heck does that work?
- Persisting data across FFI, like if you have some Go code that calls Rust, and you want the Rust code to persist stuff in memory, that gets tricky fast.
- Assumptions around strings. Go will give you a bag o' bytes and say "it's a string, trust me!" while Rust expects UTF-8.
We definitely ran into some of these issues, like remembering who should deallocate which memory.
C has many subtle behaviors, and a type system that doesn't describe them well. It also can't express its API's ownership or thread-safety, and there's no true immutability. Correctness of the ABI isn't enforced in any way, so you just smush symbols together and hope for the best. That is ripe for errors, especially when you try to add extra guarantees on the Rust side that C may or may not actually provide.