> It turns out the way that Rust “wants” to be written is overall a pretty good way for you to organize the relationships between parts of a program
That's what it promised not to do, though! Zero cost abstractions aren't zero cost when they force you into a particular design. Several of the cases in the linked article involve actual runtime and code size overhead vs. the obvious legacy/unchecked idioms.
You can go crazy with legazy/unchecked/unsafe stuff if you want to in Rust. It's less convenient and more difficult than C in some ways, but 1) it's also safer and more convenient in other ways, and 2) "this will be safe and convenient" isn't exactly the reason we dive into legacy/unchecked/unsafe stuff.
And of course the greatest strength of the whole Rust language is that folks who want to do crazy unsafe stuff can package it up in a safe interface for the rest of us to use.
The first example in the linked article is checking if a value is stored in a container and doing something different if it's not than if it is. Hardly "crazy unsafe stuff".
I think there's an important distinction here. A systems programming language needs to have all the max speed / minimum overhead / UB-prone stuff available, but that stuff doesn't need to be the default / most convenient way of doing things. Rust heavily (both syntactically and culturally) encourages safe patterns that sometimes involve runtime overhead, like checked indexing, but this isn't the same as "forcing" you into these patterns.
And I can only repeat verbatim: The first example in the linked article is checking if a value is stored in a container and doing something different if it's not than if it is.
It’s sad to see your comment this far in the thread after having had go be coxed to make it and after all the usual defensive arguments from the Rust crowd. It’s a real issue that it’s so hard to make the Rust community admits that obvious flaws actually exist.
As someone who spent a significant time working on static analysers and provers, it annoys me to no end how most of the Rust community will happily take what the borrow checker imposes on them as some form of gospel and never questions the tool. It’s bordering on Stockholm syndrome sometimes.
I think a lot of pushback is because people are talking past each other.
The reality:
- the borrow checker has limitations that doesn't accept some constructs that could be be proved safe, given Rust's own rules
- the borrow checker is a net positive as it does push you towards better constructs, and the (rare) times it forbids you from doing something that could be safe, you have safe (sometimes not zero-cost) and unsafe escape hatches
But these are then understood by the intransigent as:
- the borrow checker is always detrimental
- the borrow checker can do nothing wrong
At that point, no one can understand why the other person is being obtuse, and you end up with... well, the comment section under every Rust article.
Rust absolutely forces you into a particular design - that's what the borrow checker is about.
It's definitely possible to write correct code (in unsafe Rust or other languages) that wouldn't satisfy the borrow checker.
Rust restricts you, and in turn gives you guarantees. Zero cost only means that you don't pay extra (CPU/memory) at runtime, whereas in interpreted/GC languages you do.
> Zero cost only means that you don't pay extra (CPU/memory) at runtime, whereas in interpreted/GC languages you do.
But... again, you do. The examples in the linked articles have runtime overhead. Likewise every time you wrap something in with Box or vec or Arc to massage the lifetime analysis, you're incurring heap overhead that is actually going to be worse than what a GC would see (because GC's don't need the extra layer of indirection or to deal with reference counts).
It's fine to explain this as acceptable or good design or better than the alternatives. It's not fine to call it "Zero Cost" and then engage in apologia and semantic arguments when challenged.
We should distinguish code that's correct using the borrowing metaphor but won't pass borrowck in current Rust (such code will inevitably exist thanks to Rice's Theorem) from code that's not correct under this metaphor but would actually work, under some other model for reference or pointer types.
Because Rust is intended for systems programming it is comfortable (in unsafe) expressing ideas which cannot be modelled at all. Miri has no idea how the MMIO registers for the GPIO controller work, so, too bad, Rust can't help you achieve assurance that your GPIO twiddling code is correct in your 1200 byte firmware. But, it can help you when you're talking about things it does have a model for, such as its own data structures, which obey Rust's rules (in this case the strict provenance rule) not "memory" that's actually a physical device register.
That's what it promised not to do, though! Zero cost abstractions aren't zero cost when they force you into a particular design. Several of the cases in the linked article involve actual runtime and code size overhead vs. the obvious legacy/unchecked idioms.