Rust, on the other hand, requires the programmer to give the compiler more information about what they're doing.
A very simple example:
void foobar(struct foo *f)
{
f->a += 2;
foo();
f->a += 2;
bar();
f->a += 2;
}
Because C pointer types are so barebones, the compiler can't tell whether foo() and bar() can modify f->a just from looking at the above code. So it will always have to load and store that for each += operation.Rust on the other hand has two kinds of reference, rather than pointers:
fn foobar(f : &mut foo) {
f.a += 2;
foo();
f.a += 2;
bar();
f.a += 2;
}
This is more high-level. But it's good for performance! Rust has a rule that you can only have one mutable reference to a struct at one time. Therefore, foo() and bar() can't be modifying f.a and it can simplify this to `f.a += 6;`.(You can see it in action for yourself here: https://godbolt.org/z/hWs67P. Sadly, Rust doesn't do this by default due to problems with LLVM, but eventually it can.)
I've also used them on occasions. They do a particular job people call pluggable implementations / dynamic typing / sub-classing depending on their background. If there isn't some central place that knows all the types (if there is you can use switch/case/match), there isn't much choice, it's vtables or nothing. But needing to do that job is rare, so seeing it in languages that don't use vtables promiscuously to implement OO is correspondingly rare.
Which is as it should be, because vtables are more expensive than a direct call, very expensive if they prevent inlining. One of rust's triumphs is it provides them, but you have to go out of your way to use them so it encourages avoiding their overheads.
Can it, though? I keep hearing/reading FUD around both unsafe and Pin making it unlikely to ever be able to ubiquitously enable the noalias stuff.
Are there issues I can subscribe to or RFCs for this?
Bug report at https://github.com/rust-lang/rust/issues/63818.
Reddit threads at (older) https://www.reddit.com/r/rust/comments/l4roqk/a_fix_for_the_... and (newer) https://www.reddit.com/r/rust/comments/lxw6cl/update_to_llvm....
Somewhat related HN thread at https://news.ycombinator.com/item?id=26406989.
The HN thread is mostly unrelated. I agree that it would have been better to integrate `Pin` directly into the language, though, but mostly for ergonomic reasons; it could still probably happen in an edition upgrade.
void foobar(struct foo *restrict f) { ... }> There's a significant difference between what these languages can achieve in theory, and how they're used in practice.
Theoretically in C you can use restrict for this. Practically nobody does (rust users keep finding bugs and miscompilation in LLVM's noalias support), and it's a huge footgun because you're completely on your own and all bets are off if you misuse it.
Meanwhile in rust land, it's the default and the compiler checks your homework, and while you can always lie to the compiler it's much rarer that you'd even get in a position to do so.
There's a very real difference between what's possible in theory and what humans do in practice. I wish Hacker News people engaged more with the practice.
But I think it's pretty uncommon in practice. One reason is that the C compiler has no borrow checker to help you notice when you're using `restrict` unsafely.
(Also, there's the whole “strict aliasing” hell…)
Note, I genuinely don't know if this caveat applies to Rust. But, m general, not only mutable references need to be considered.
The exact same argument applies to assembly code. There are very good reasons that it's not used nowadays except in incredibly rare circumstances or in the embedded world.
It doesn't matter in the slightest how fast your language is in theory. Not one iota.
The only thing that matters is how fast the programs you write with it are in practice. The evidence is clear: it is significantly easier to write faster programs in Rust than it is in C, and this applies to even the most skilled developers.
There are many things which cause both frequent and rare strange crashes in multi-threaded code.
In C, all these things will compile OK. In safe Rust, practically none of them will compile.
It is much easier to fix compile issues in Rust to do with dangerous memory usage or thread sharing than it is to debug a compiled program.
If you need to do it across processes, then you need a file system that supports exclusive file opens.
The db question is a red-herring, because to properly solve for concurrency in the db, you need to use locks in the db (table and row locks), not the code accessing the db.
Nit: rust will prevent all manner of bugs. Rust won't prevent every manner of bugs.
Please stop moving the goal posts from “program concurrency” to “distributed or multi-process transactions.” It subtracts from the conversation.
Rust is honest about what it does and doesn’t do.
I am not moving goal posts, rather talking about issues that many apparently lack the knowledge to understand how many variants of data races exist in an application.
Quite understanable given the majority of developers that keep writing single threaded applications.
https://blog.knoldus.com/how-we-can-do-fearless-concurrency-...
Now rewrite the same blog post using SQL queries to update the same counter variable on a table row.
The Rustonomicon is quite clear that it wouldn't work,
> However Rust does not prevent general race conditions.
> leaving to the reader that it works the same way regardless of what resources are being accessed.
If that's how readers envision that non-language-managed resources work, they should go back to CS102 right now.
And concerning the nomicon, it explicitly states that Rust can't handle all races condition, I don't get your point.
But you're right to point out that Rust won't magically solve all your possible sources of crash and data corruption, it just does make it a lot easier to enforce arbitrary constraints. For instance if you decide that in order to avoid these issues you want all your DB stuff to only run in a single thread you could design your API in a way that would prevent DB handles from being shared between threads, all that enforced by the language. You couldn't accidentally leak a DB handle to a different thread.
All the rust db interfaces I've seen so far forbid unlocked sharing of connections, statically (the connections are !Sync, in Rust speak).
This is a fairly odd claim.
If you have to deal with strings (ASCII and UTF-8) properly, C is stupidly verbose.
If you need a data structure more complex than an array of something, C is stupidly verbose.
If you want to deal with pattern matching/regexen, C is ridiculously verbose.
Do I agree that Rust is far more verbose for an embedded "blinky" (the embedded equivalent of "Hello, World!")? Yes.
But once I start doing things like processing messages in a communications stack (BLE, CANOpen, Ethernet, etc.), Rust starts looking better and better.
// Main function
#[arduino_uno::entry]
fn main() -> ! {
let peripherals = arduino_uno::Peripherals::take().unwrap();
let mut pins = arduino_uno::Pins::new(
peripherals.PORTB,
peripherals.PORTC,
peripherals.PORTD,
);
// Pin D13 is connected to L led
let mut led = pins.d13.into_output(&mut pins.ddr);
loop {
led.toggle().void_unwrap();
arduino_uno::delay_ms(500);
}
}
https://github.com/vlisivka/rust-arduino-blink/blob/main/src...Are you just noting specifics, or was it your experience that there was less Rust required for a full C to Rust conversion?
> If you need a data structure more complex than an array of something, C is stupidly verbose.
What’s stupidly verbose about Structs to you?
Every one of those things requires a fairly primitive function call that sometimes doesn't even need to exist in another language. For contrast, think about all the macrology that the Linux kernel does to iterate across every element of a linked list that is effectively "for element in list {}" in any higher language.
And, even worse, most of that stuff in C needs to be runtime-only, while some of this kind of thing gets elided by the compiler in other languages.
If you don't consider all that "stupidly verbose" I'm really curious what you would?
And this is long before we start talking about concurrency.
C allows you to write something that works "just enough" that you put it into production and then it bites you in the ass (similar to quadratic algorithms--functional enough to get to production and then bite you).
It's not that it's easier to write programs in [Java|Rust]. It's that it makes it much harder to write the bugs.
Writing programs in C is harder just because there are essentially no containers in the stdlib.
It's doable, but not very easy.
Once you give up the closed source/prebuilt binary library idea and embrace the C++-like header library idea and write implemenations in terms of "assumed macro/inline function" definitions, the problem becomes straightforward with no performance issue and different ergonomics issues than you probably think.
It's a more "manual instantiation" than C++ templates or generics in other languages where just refering to them works, but most of C is quite manual. So, it fits the headspace & the hard parts of data structures/meddlesome hands remain factored out. Since you parameterize your files/code with #define/#include, you have to name your parameters which can make the instantiating client code more obvious than C++ templates with many arguments. OTOH, there is no/poor type checking of these parameters.
Having a type declaration dependent on #define P whether it is plain old data or not, and needing to know what that means, is not the kind of ergonomics I'd want. That requires learning a whole new paradigm to ensure I am not doing wrong things.
In my mind it is so big an extension of the C language, that it leaves the C headspace and becomes its own headspace.
It is a smaller learning curve from pure C than "all of Rust" or even "all of C++/STL". You got the basic idea in short order (that may be for ill as well as good..I was never trying to make a normative claim).
But it’s not just taking care of those things that must be done; it seems to bloat the code.
I think it’s a great alternative to C for large apps like Firefox, and perhaps it’s a good alternative to Go for services.
For general purpose, I personally want something fast that’s easy, clear, and concise like Ruby.
Do I just accept that Rust is the most evolved version of C, or is my gut correct that it’s bloated? Is it a good choice for general purpose as-is?
The things I find most difficult: 1. Wrangling with the borrow checker can be painful before you know what you're doing (and even afterwards) but if you understand the standard library/patterns well, it seems to minimize the cost. Example being trying to write your own `get_or_else_insert` style method for a HashMap. Writing your own version is easy in other languages but hard in Rust. If you didn't know that methods like that already exist on HashMap you will experience a lot of pain until you understand the "right" way to do something.
2. Shared memory concurrency is definitely at the nexus of all the more difficult parts of Rust. Especially with async/await. There's no question in my mind that if you want to write a webserver that has async functions accessing shared memory, that you will for sure need to fill in any gaps in your knowledge as it will be difficult to get to a working program without understanding significantly more concepts than what it might take for a simple single-threaded CLI app
I'm pretty sure that it will be possible (if it isn't already) to get the Rust ecosystem to a state where writing a CRUD app is about as simple as in Go (and considerably easier/more ergonomic w.r.t certain things like JSON serialization).
It's not so much that they keep adding features, but that they (almost?) never remove any.
If you mean GHC, the language version is whatever the pile of configuration flags at each source file ends up meaning, some of them even contradict themselves.
Great for language research, which is Haskell main purpose in life, hardly a good idea for getting industry love.
About GHC: I think their approach with pragmas is great and something for other languages to emulate. It's also great in production, with the caveat that you might want to restrict that mechanism to surface level changes only, and nothing that changes the intermediate format.
It doesn't really, some things from C won't compile in C++, but that's a minor nitpick
My experience is mostly the opposite. For many tasks Rust can be much more concise (see anything with string handling, for example).
I haven't seen that in practice. A good point of reference are implementations of things like ruby, python, or the erlang vm in rust compared to the C alternatives. This might be because Rust is also more expressive (probably by borrowing certain syntax/semantics from ocaml/haskell), though the borrow checker does add back some verbosity.