> Saying these features could appear everywhere is no difference from "unsafe" possibly appearing everywhere in Rust.
That's not true in practice: Unsafe code is clearly delineated and can be 100% correctly identified. In C, usage of dangerous features can occur at any point and is much harder to clearly separate.
First, if "unsafe" worked so well 100% time, why did we have this bug? (and many other) So this already obviously wrong statement.
Then yes, you can use dangerous features in C at any time, but obviously you can also use "unsafe" at any time. The only difference is that "unsafe" is clearer to recognize. But how much this is worth is unclear. First, if you do not invalidly reduce the discussion to only memory safety, you need to review all other code anyway! But even then, it is also not true that only the code marked with "unsafe" is relevant. This is major myth. The "unsafe" code can cause UB outside "unsafe" and logic bugs outside "unsafe" can cause bugs unsafe. This does not perfectly decouple if you Rust repeat this nonsense over and over again.
Don't get me wrong, I think the unsafe keyword is good idea. But the magical powers Rust fans attribute to it and the "SAFETY" comment they put next to it tells me they are a bit delusional.
> logic bugs outside "unsafe" can cause bugs unsafe.
This is the wrong understanding of Rust's unsafety encapsulation. For example, no logic bug outside of `unsafe` can cause undefined behavior of Rust std's `Vec` abstraction, which is using underlying unsafe to build.
The point that "because unsafe is used so the entire Rust program is also unsafe" is a real major myth. It's as absurd as saying "because Java runtime using unsafe underlying to build so Java is also unsafe".
Why was the fix to this unsafe memory safety bug [0] only changes to code outside of unsafe Rust blocks?[1][2]
Why does the Rustonomicon[3] say the following?
> This code is 100% Safe Rust but it is also completely unsound. Changing the capacity violates the invariants of Vec (that cap reflects the allocated space in the Vec). This is not something the rest of Vec can guard against. It has to trust the capacity field because there's no way to verify it.
> Because it relies on invariants of a struct field, this unsafe code does more than pollute a whole function: it pollutes a whole module. Generally, the only bullet-proof way to limit the scope of unsafe code is at the module boundary with privacy.
If a logic bug inside Vec and outside any unsafe blocks inside Vec happens, and that logic bug violates any invariants and requirements of the unsafe blocks, that can cause memory unsafety.
That would be the unsoundness of `Vec` itself, but if the abstraction of `Vec` is sound, there would be no way to use `Vec` outside of `unsafe` that can cause memory unsafety.
The point coming back to abstraction and responsibility, in Rust, you can build abstraction that is sound and guarantee memory safety from there. There can be soundness bug inside your abstraction, but it will be a massively smaller surface for auditing and expert required to write such abstraction. Also, when soundness bug appears, the responsibility is solely on the abstraction writer, not the user.
Whereas in C, without those safe abstraction, the surface of doing thing right to avoid memory safety issue is your entire codebase, and responsibility of "holding the knife correctly" is on the user.
> There can be soundness bug inside your abstraction, but it will be a massively smaller surface for auditing and expert required to write such abstraction.
If all of the Vec has to be "audited", or checked and reviewed, including all the code that is not inside unsafe blocks, how would the surface be any smaller?
> The point coming back to abstraction and responsibility, in Rust, you can build abstraction that is sound and guarantee memory safety from there.
Isn't it normal for programming languages to support building abstractions that can help with not only memory safety, but general correctness? C is a bit barebones, but lots of other programming languages, like C#, C++, Haskell and Scala support building abstractions that are harder to misuse.
All of `Vec` is much smaller than all of the place using Vec. IIRC, Vec is around 3k LoC. And for even low level code like Oxide & Android core, they are observed less than 4% of their code is inside or related to unsafe, that’s a massive improvement.
Yes, Rust is not new in term of allow building hard to misuse abstraction, it’s just allow abstraction over memory safety without relying on GC or runtime checks.
Rust achieve this by adding capability to enforce shared XOR mutability with its borrowck which C++ couldn’t.
> It’s mostly C and C++ developers trying to build a new option for solving their same problems.
I've observed that a lot of the folks I used to meet at ruby conferences have moved to Rust. No idea what led to this, but maybe it's just folks that were generally curious about new programming languages that moved to ruby when it became better known and that the same interest led to adopting Rust.
I worked on a Ruby codebase that moved to Rust - I think that part is mostly cargo-culting cool things in the news to be perfectly honest. There’s type safety advantages, but if Ruby’s performance envelope was even conceivably acceptable for your use-case there are likely better options. I strongly suspect a lot of the friction between Rust and only-C/C++ developers is the product of a bunch of people coming at Rust from higher level languages parroting lines about safety, and then looking at you blankly if you ask how it handles nested pointer indirection.
But I don’t think that applies to the people actually driving the language forward, just those talking a big game on HN/Reddit.
Your iphone is tied to the old apple account and you can't untie it if you can't access the old account. (You can go through support with proof of purchase, but that requires you have proof of purchase at hand etc.)
Fun thing is that almost every other CI as a service provider charges you in some shape or form for self hosted runners. CircleCI limits the number of self-hosted Job Running in parallel based on your plan and charges a fixed base fee per seat.
So moving away from GHA will not make self-hosted runners free, they’ll move into a different pricing structure that may or may not be beneficial.
And I think charging for self-hosted runners is actually fine. They’re not free for the provider either - log aggregation, caching of artifacts, runner scheduling, implementing the runner software etc are non-trivial problems for any larger CI system.
So I’m actually fine with the proposed change since it also gives me the power as a customer to say “hey, I’m paying for this, fix it.”
The problem is that they are charging a per-minute fee, and a fee at the same order-of-magnitude as actually running the tests. If you're offering cloud-hosted runners for $0.002/minute, asking that same $0.002/minute as an orchestrator fee for self-hosted runners is just insulting.
Charging for self-hosted runners is indeed not a huge deal, and I bet they wouldn't have gotten the same kind of backlash if they charged for it via a per-seat, per-run, per-gigabyte, or per-log-line fee. And if GHA hadn't been so poorly maintained...
Any model that charges for self-hosted runner is going to feel unfair to someone. Per seat pricing is better for small orgs with a lot of CI minutes, per-run pricing would be good for orgs with few, long runs, per minute pricing is nicer for orgs with many small runs.
In my observation the critisicm was strongly dominated by outrage over the actual fact.
I also think its fine and fair to charge for the general GHA infrastructure that one would also be using with self-hosted runners.
I suspect that they weren't looking to make money off of those charges, but rather use that as a forcing function to push more usage of their managed runner (which are higher margin) which didn't work out.
Rather than everyone saying "damn that makes alternatives financially unattactive", a good chunk of the feedback was "sure I'll pay those charges as long as I don't have to use the shitty managed runners".
Depends if they are using another CI provider or running Jenkins themselves.
But also, Circle CI would be a known cost change. Right now, the only thing you know is that GitHub wants to start charging money. You have no idea what new pricing model they come up with.
Self-hosting all of your CI is yet another tradeoff. The software comes for free (if you're using Free Software, that is), but you now have operational overhead. I'm not saying it's an unreasonable move, but it's also not a free swap
The cost of the control plan for Github and the cost of their runners are not equal. Yet this new plan seems to say a self-hosted minute is counted the same as a hosted minute, since self-hosted minutes count towards the 2000 included minutes.
Gitlab enters the room, where self-hosted runners are as free as in free beer (maintenance yes, but no limit on runners and no pricing expect on a per-user basis).
Yes, gitlab does still have free self-hosted runners. OTOH, github has a free organisation plan and gitlab doesn't. So yes, strictly speaking self-hosted runners are free, but you're paying for the dev-seats.
> So I’m actually fine with the proposed change since it also gives me the power as a customer to say “hey, I’m paying for this, fix it.”
I’m paying for GitHub Action now and there is zero recourse (other than leaving). Giving them money doesn’t change anything.
I’d be more willing to pay if GH Actions wasn’t so flakey and frustrating (for hosted or self-hosted runners, I use both). At least self-hosted runners are way cheaper _and_ have better performance.
> I’d be more willing to pay if GH Actions wasn’t so flakey and frustrating (for hosted or self-hosted runners, I use both).
This is indeed a reason I do consider leaving GHA. The underinvestment into this part of the product shows. But they also did announce quite some investment into new and (for us relevant) features alongside the pricing change, so I'll have a look at how this changes with some sorely needed work on the product.
How do you pay? Because the basic organization plan is free and gives access to GHA and includes 2000 free minutes.
If you upgrade the plan, you get more minutes for free - which can be consumed by the cost for free runners. They haven't specified at which rate a self-hosted runner consumes the free minutes, but at least for us, the change will largely consume free minutes.
> You might as well say that we should be paying per PR and Issue because, well, that part can’t just be free, you know?
You're misrepresenting what I said. I said, I'm fine with this for these reasons. It's a statement about me, not about what you should do nor what you should consider fine.
I pay (quite a bit) for GH because I do receive a service that's worth it, at least for now. And I'd rather see that GHA is something that makes them money than become something that is second-rate and lingers, just as it did before they made this announcement.
It certainly would be illegal in Europe under the GDPR - data collected for one purpose (handling of applications) cannot be used for another without explicit, informed consent.
It may be illegal, but shady stuff certainly happens in EU too.
Recently investigative journalists here in Finland found out that a significant percentage of job postings over here are indeed fake. Unsurprisingly, worst offenders were recruitment companies, which sometimes listed fake jobs to generate a pool of applicants they can later offer to their clients. Doing this is easy, as no law requires these companies to disclose who their clients are when creating job postings. It's also very common for same position to get posted multiple times.
Other than wasting applicant's time, this behavior also messes up many statistics, which use job postings to determine how many open positions there are available. Basically the chances of finding a job are even worse for unemployed people than stats would imply.
Oh, I agree that illegal stuff happens in the EU aplenty. The question was whether this is illegal and it almost certainly is. Job postings from recruiters - while morally reprehensible - may just skirt the law, but straight up using applications for profile building, marketing and other purposes almost certainly isn’t permitted
The box would need to be off by default and clearly state the purpose. It would at least be possible to verify it exists and no example has been shown.
I can tell you that our HR takes the time to write proper responses to every application, but we straight up delete AI written applications. I honor the effort put into any application, but if people haven’t spent the time, then why should we?
Your HR is the exception though, not the rule. I'm willing to risk this, since it allows me to keep my sanity. And if a company really seems awesome I will still put in the effort.
When we post a job ad on LinkedIn, I already get 30 spam answers in the first few minutes, people that have obviously spent not a single second reading the requirements. And you want to further automate this? I’ll just ask my people person to automate the response.
We really try to spend the time to answer every application, but since AI generated applications have become a thing, we have decided to not answer those. Why should I spent time if you haven’t spent the time?
Sorry about that! I apply to those jobs because you put out some insane requirements, and I assume you're not actually looking for a single SME with expertise across half a dozen distinct domains and a decade of experience in tech that hasn't existed for half that time.
Unless, you are actually hoping to find a full stack developer that can also serve as the principal engineer for your entire network, storage, VMware infra, plus support basically anything with cord all for ~$80-100k.
Normally when you get to the point of discussing things with a human, you find out what the actual job is. The job ad is almost always completely irrelevant to the actual job.
Standardized data formats would greatly reduce the amount of weeding. The candidate should have compared your wants to their skills already, you cross-check their listed skills vs your wants.
And we need to sort out needs vs wants. Job postings should include the required skills (do not submit if you don't meet them) and a separate listing for additional skills that would be desirable. Don't waste everyone's time with a guessing game where people need to decide if they are close enough to the wish list.
The spammers spend almost no time filling any application, no matter how much work is intended. They have scripts for that. Granted, making it standardized means less skilled spammers can spam too.
Making it difficult to apply reduces the number of legitimate applications, however.
That’s a great idea if you’re hiring locally but we do hire international remote and I suspect no one will snail-mail me an application from the Americas or South Africa to Germany.
I’m amazed by the idea that providing escape capsules would have saved many lives. The Christmas tsunami caused about 230 000 fatalities in a densely populated area. People didn’t even get to higher ground. Where are you going to store the hundreds of thousands capsules that you’d need to even make a dent in that number. And how will people get into those capsules within minutes of the warning?
And who is going to find all those capsuled people and rescue them? Rescuers will be swamped with hundreds of thousands of non-capsuled people who should logically take priority. Depending on how these things float, if they get swept out to sea, you might need a ship with a crane to lift the capsule aboard. Does it float nicely with the hatch open or do you have to stay sealed up to stay afloat? Can you float with air ports open or do all of you have to stay breathing that scuba tank in the photo; how long will that last? What will many -- thousands? -- of EPIRBs all going off at once do to the SAR system?
I don't think that is the business model: there will be a small percentage of people able to afford these, which will have transponders and 'priority rescue' status from emergency services, as part of the subscription package.
Yes, sure. But even that wouldn’t work - those people still need to have advanced warning and be close to a capsule. But if you’re close to a capsule, then you’re likely close to home and you can just build a flood safe room in your house.
Escape capsules is probably over complicating things. An inflatable life raft would probably be more practical. You can get them with gas cylinders so they inflate in a few seconds.
I can't imagine this would work. Can you not remember the footage of the wall of houses/cars/boats/trees/etc moving across the landscape? Your raft is getting flattened.
If you need glibc for any kind of reason, that approach is still used. But that won’t save you if no glibc is available. And since the folks here want to produce a musl build anyways for alpine, the easier approach is to just go for musl all the way.
It's great that the Google Android team has been tracking data to answer that question for years now and their conclusion is:
-------
The primary security concern regarding Rust generally centers on the approximately 4% of code written within unsafe{} blocks. This subset of Rust has fueled significant speculation, misconceptions, and even theories that unsafe Rust might be more buggy than C. Empirical evidence shows this to be quite wrong.
Our data indicates that even a more conservative assumption, that a line of unsafe Rust is as likely to have a bug as a line of C or C++, significantly overestimates the risk of unsafe Rust. We don’t know for sure why this is the case, but there are likely several contributing factors:
unsafe{} doesn't actually disable all or even most of Rust’s safety checks (a common misconception).
The practice of encapsulation enables local reasoning about safety invariants.
The additional scrutiny that unsafe{} blocks receive.
> The practice of encapsulation enables local reasoning about safety invariants.
> The additional scrutiny that unsafe{} blocks receive.
None of this supports an argument that "unsafe Rust is safer than C". It's just saying that with enough scrutiny on those unsafe blocks, the potential bugs will be found and addressed as part of development. That's a rather different claim.
It does, if you read the report and run a little (implied) math.
The report says that their historical data gives them an estimate of 1000 Memory Safety issues per Million Lines of Code for C/C++.
The same team currently has 5 Million lines of Rust code, of which 4% are unsafe (200 000). Assuming that unsafe Rust is on par with C/C++, this gives us an expected value of about 200 memory safety issues in the unsafe code. They have one. Either they have 199 hidden and undetected memory safety issues, or the conclusion is that even unsafe Rust is orders of magnitude better than C/C++ when it comes to memory safety.
I trust them to track these numbers diligently. This is a seasoned team building foundational low level software. We can safely assume that the Android team is better than the average C/C++ programmer (and likely also than the average Rust programmer), so the numbers should generalize fairly well.
Part of the benefits of Rust is indeed that it allows local reasoning about crucial parts of the code. This does allow for higher scrutiny which will find more bugs, but that's a result of the language design. unsafe {} was designed with that im mind - this is not a random emergent property.
They say "With roughly 5 million lines of Rust in the Android platform and one potential memory safety vulnerability found (and fixed pre-release), our estimated vulnerability density for Rust is 0.2 vuln per 1 million lines (MLOC).".
Do you honestly believe that there is 1 vulnerability per 5 MLoC?
1 memory safety vulnerability, that's a pretty important distinction.
Yes, I believe that at least the order of magnitude is correct because 4 800 000 of those lines are guaranteed to not have any by virtue of the compiler enforcing memory safety.
So it's 1 per 200 000, which is 1-2 orders of magnitude worse, but still pretty darn good. Given that not all unsafe code actually has potential for memory safety issues and that the compiler still will enforce a pretty wide set of rules, I consider this to be achievable.
This is clearly a competent team that's writing important and challenging low-level software. They published the numbers voluntarily and are staking their reputation on these reports. From personal observation of the Rust projects we work on, the results track with the trend.
There's no reason for me to disbelieve the numbers put forward in the report.
You accidentally put the finger on the key point, emphasis mine.
When you have a memory-unsafe language, the complexity of the whole codebase impact your ability to uphold memory-related invariants.
But unsafe block are, by definition, limited in scope and assuming you design your codebase properly, they shouldn't interact with other unsafe blocks in a different module. So the complexity related to one unsafe block is in fact contained to his own module, and doesn't spread outside. And that makes everything much more tractable since you never have to reason about the whole codebase, but only about a limited scope everytime.
No, this is just an example of confirmation bias. You're given a totally unrealistic figure of 1 vuln per 200K/5M LoC and now you're hypothesizing why that could be so. Google, for anyone unbiased, lost the credibility when they put this figure into the report. I wonder what was their incentive for doing so.
> But unsafe block are, by definition, limited in scope and assuming you design your codebase properly, they shouldn't interact with other unsafe blocks in a different module. So the complexity related to one unsafe block is in fact contained to his own module, and doesn't spread outside. And that makes everything much more tractable since you never have to reason about the whole codebase, but only about a limited scope everytime.
For anyone who has written low-level code with substantial complexity knows that this is just a wishful thinking. In such code, abstractions fall-apart and "So the complexity related to one unsafe block is in fact contained to his own module, and doesn't spread outside" is just wrong as I explained in my other comment here - UB taking place in unsafe section will transcend into the rest of the "safe" code - UB is not "caught" or put into the quarantine with some imaginative safety net at the boundary between the safe and unsafe sections.
Let's take a simple example to illustrate how unsafe {} cuts down the review effort for many operations. Take a static mutable global variable (a global counter for example). Reading a static is safe, mutating it (increasing the counter) is not - it requires an unsafe {} block.
If you need to check which places mutate this global static you only need to check the unsafe parts of the code - you know that no other part of your code could mutate it, the compiler won't let you. If you have a bug that is related to mutating this static, then it might manifest anywhere in your code. But you know for certain that the root cause must be in one of your unsafe blocks - even if you don't know which one.
Good programming practice will cut down that effort even more by dictating that unsafe access should be grouped in modules. For example when binding to a C module (unsafe) you'd usally generate an unsafe wrapper with bindgen and then write a safe wrapper on top of that. Any access that tries to go around the safe wrapper would be frowned upon and likely fail review.
And again, the compiler will help you there: Any access that tries to bypass the safe api would need to be unsafe {} again and automatically receive extra scrutiny in a review, making it less likely to slip through.
Compare that to a C codebase where anything goes. A static might be mutated anywhere in your codebase, even through a pointer to it - meaning you can't even reliably grep for it. It may slip through review unnoticed because no attention is drawn to it and cause bugs that are hard to trace and reason about.
If you're writing embedded code, similar considerations apply - access to registers etc. require unsafe {}. But because access is unsafe {}, it's usually gated behind a safe api that is the boundary of the low-level code and the higher buisness logic. Unsurprisingly, these are critical parts of the code - hence they receive extra scrutiny and in our project, we allocate substantial review capacity on those. And the compiler will enforce that no safe code can circumvent the access layer.
The number you're tagging as unrealistic figure is the result of dedicated and careful design of language and compiler features to achieve exactly this outcome. It's not a random fluke, very clever people did sit down and thought about how to achieve this.
> You're given a totally unrealistic figure of 1 vuln per 200K/5M LoC and now you're hypothesizing why that could be so.
You are the one claiming it's unrealistic. And you gave zero argument why besides “the codebase is complex”, which I refuted. See the definition of complexity:
> The term is generally used to characterize something with many parts where those parts interact with each other in multiple ways, culminating in a higher order of emergence greater than the sum of its parts
Each unsafe block may be “difficult” in itself, but the resulting system isn't “complex” because you don't have this compounding effect.
> I wonder what was their incentive for doing so.
And obviously it must be malice…
> For anyone who has written low-level code with substantial complexity knows that this is just a wishful thinking. In such code, abstractions fall-apart and "So the complexity related to one unsafe block is in fact contained to his own module, and doesn't spread outside" is just wrong as I explained in my other comment here - UB taking place in unsafe section will transcend into the rest of the "safe" code - UB is not "caught" or put into the quarantine with some imaginative safety net at the boundary between the safe and unsafe sections.
I think you don't understand the problem as well as you think you do. Of course if the UB happens then all bets are off! Its consequences won't be limited to a part of the code, by definition. And nobody said otherwise.
But for the UB to happen, there must be some violation of an memory invariant (the most common would be using a value after free, freeing twice, accessible the same memory from multiple threads without synchronization or, and this is specific to Rust, violating reference aliasing rules).
To avoid violating these invariants, the programmer must have a mental model of the ownership over all the system on which these invariants apply. For C or C++, it means having a mental model of all the code base, because the invariants related to one piece of code can be violated from everywhere.
In Rust this is different, you're not going to have raw pointers to one piece of data being used in multiple parts of the code (well, if you really want, nobody stops you, but I'm confident the Android team didn't). And as such, you'll have to think about the invariants only at the scale of one module. Building an accurate mental model of a 350-line module is much more tractable for a human than doing the same for an entire codebase, and it's not even close.
That's the other interesting observation you can draw from that report. The numbers contained in the first parts about review times, rollback rates, etc. are broken down by change size. And the gap widens for larger changes. This indicates that Rusts language features support reasoning about complex changesets.
It's not obviously clear to me which features are the relevant ones, but my general observation is that lifetimes, unsafe blocks, the borrow checker allow people to reason about code in smaller chunks. For example knowing that there's only one place where a variable may be mutated supports understanding that at the same time, no other code location may change it.
It actually does support it. Human attention is a finite resource. You can spend a little bit if attention in every line to scrutinize safety or you can spend a lot of time scrutinizing the places where you can't mechanically guarantee safety.
It's safer because it spends the human attention resource more wisely.
The practice of encapsulation enables local reasoning about safety invariants.
which is not fully correct. Undefined behavior in unsafe blocks can and will leak into the safe Rust code so there is nothing there about the "local reasoning" or "encapsulation" or "safety invariants".
This whole blog always read to me as too much like a marketing material disguised with some data so that it is not so obvious. IMHO
> which is not fully correct. Undefined behavior in unsafe blocks can and will leak into the safe Rust code so there is nothing there about the "local reasoning" or "encapsulation" or "safety invariants".
Strictly speaking, that encapsulation enables local reasoning about safety invariants does not necessarily imply that encapsulation guarantees local reasoning about safety invariants. It's always possible to write something unadvisable, and no language is capable of preventing that.
That being said, I think you might be missing the point to some extent. The idea behind the sentence is not to say that the consequences of a mistake will not be felt elsewhere. The idea is that when reasoning about whether you're upholding invariants and/or investivating something that went wrong, the amount of code you need to look at is bounded such that you can ignore everything outside those bounds; i.e., you can look at some set of code in complete isolation. In the most conservative/general case that boundary would be the module boundary, but it's not uncommon to be able to shrink those boundaries to the function body, or potentially even further.
This general concept here isn't really new. Rust just applied it in a relatively new context.
Yes, but my point is when things blow up how exactly do you know which unsafe block you should look into? From their statement it appears as if there's such a simple correlation between "here's your segfault" and "here's your unsafe block that caused it", and which I believe there isn't, and which is why I said there's no encapsulation, local reasoning etc.
> Yes, but my point is when things blow up how exactly do you know which unsafe block you should look into?
In the most general case, you don't. But again, I think that that rather misses the point the statement was trying to get at.
Perhaps a more useful framing for you would be that in the most general case the encapsulation and local reasoning here is between modules that use unsafe and everything else. In some (many? most?) cases you can further bound how much code you need to look at if/when something goes wrong since not all code in unsafe modules/functions/blocks depend on each other, but in any case the point is that you only need to inspect a subset of code when reasoning about safety invariants and/or debugging a crash.
> From their statement it appears as if there's such a simple correlation between "here's your segfault" and "here's your unsafe block that caused it",
> in the most general case the encapsulation and local reasoning here is between modules that use unsafe and everything else
This would be the same narrative as in, let's say, C++. Wrap the difficult and low-level memory juggling stuff into "modules", harden the API, return the references and/or smart-pointers, and then just deal with the rest of the code with ease, right? Theoretically possible but practically impossible.
First reason is that abstractions get really leaky, and they especially get really leaky in the code that demands the upmost performance. Anyone who implemented their own domain/workload specific hash-map or mutex or anything similarly foundational will understand this sentiment. Anyway, if we just have a look into the NVMe driver above, there're no "unsafe modules".
Second, and as I already argued, UB in the module library transcends into the rest of your code so I fail to understand how is it so that the dozens of unsafe sections make the reasoning or debugging any more simpler when reasoning is actually not a function of number of unsafe sections but it is the function of interactions between different parts of the code that end up touching the memory in the unsafe block in a way that it was not anticipated. This is almost always the case when dealing with undefined behavior.
> I don't get that sense from the statement at all.
It is a bit exaggerated example of mine but I do - their framing suggests ~exactly that and which is simply not true.
> This would be the same narrative as in, let's say, C++. Wrap the difficult and low-level memory juggling stuff into "modules", harden the API, return the references and/or smart-pointers, and then just deal with the rest of the code with ease, right? Theoretically possible but practically impossible.
The difference, of course, is in the amount of automated help/enforcement provided that makes it harder/impossible to misuse said API. Just like C++ provides new functionality compared to C that makes it hard-to-impossible to misuse APIs in certain ways (RAII, stronger type system, etc.), and how C does the same compared to assembly (providing structured control flow constructs, abstracting away low-level details like calling convention/register management, etc.), Rust provides new functionality that previous widespread languages didn't. It's those additional capabilities that make previously difficult things practical.
> so I fail to understand how is it so that the dozens of unsafe sections make the reasoning or debugging any more simpler when reasoning is actually not a function of number of unsafe sections but it is the function of interactions between different parts of the code that end up touching the memory in the unsafe block in a way that it was not anticipated.
...Because the way encapsulation works in practice is that only a subset of code can "touch[] the memory in the unsafe block in a way that it was not anticipated" in the first place? That's kind of the point of encapsulation!
(I say "in practice" because Rust doesn't and can't stop you from writing unsafe APIs, but that's going to be true of any language due to Rice's Theorem, the halting problem, etc.)
As a simple example, say you have a program with one singular unsafe block encapsulated in one single function which is intended to provide a safe API. If/when UB happens the effects can be felt anywhere, but you know the bug is within the encapsulation boundary - i.e., in the body of the function that wraps the unsafe block, even if the bug is not in the unsafe block itself (well, either that or a compiler bug but that's almost always not going to be the culprit). That certainly seems to me like it'd be easier to debug than having to reason about the entire codebase.
This continues to scale up to multiple functions which provide either a combined or independent API to internal unsafe functionality, whole modules, etc. Sure, debugging might be more difficult than the single-function case due to the additional possibilities, but the fact remains that for most (all?) codebases the amount of code responsible for/causing UB will reside behind said boundary and is going to be a proper subset of the all the code in the project.
And if you take this approach to the extreme, you end up with formal verification programs/theorem provers, which isolate all their "unsafe" code to a relatively small/contained trusted kernel.Even there, UB in the trusted kernel can affect all parts of compiled programs, but the point is that if/when something goes wrong you know the issue is going to be in the trusted kernel, even if you don't necessarily know precisely where.
> It is a bit exaggerated example of mine but I do - their framing suggests ~exactly that and which is simply not true.
I do agree that that the claim in that particular interpretation of that statement is wrong (and Rust has never offered such a correlation in the first place), but it's kind of hard to discuss beyond that if I don't interpret that sentence the same way you do :/
That's not true in practice: Unsafe code is clearly delineated and can be 100% correctly identified. In C, usage of dangerous features can occur at any point and is much harder to clearly separate.
reply