The readme has a more direct explanation of this project's objective:
"MiniRust is the cornerstone of my vision for a normative specification of Rust semantics. It is an idealized MIR-like language with the purpose of serving as a "core language" of Rust. This is part of a larger story whose goal is to precisely specify the operational behavior of Rust, i.e., the possible behaviors that a Rust program might have when being executed: the behavior of a Rust program is defined by first translating it to MiniRust (which is outside the scope of this repository), and then considering the possible behaviors of the MiniRust program as specified in this document. That translation does a lot of work; for example, traits and pattern matching are basically gone on the level of MiniRust. On the other hand, MiniRust is concerned a lot with details such as the exact evaluation order, data representations, and precisely what is and is not Undefined Behavior."
It is yet another step on the path to having a Rust specification, especially with respect to precisely specifying the capabilities and requirements of `unsafe` code.
Awesome! Rusts ad-hoc "the compiler is the specification" has always been a huge turnoff for me, and a huge hurdle when learning the language.
The language is just too complex and the compiler is too large and smart/magical to really get what's going on from ad-hoc alone.
Zig is also very compiler dependent, but at least the language has direct enough translation as a goal, that it's straightforward to learn the semantics from trial and error alone.
C was invented in 1972 and became one of the most widely used languages in the world with "the compiler is the specification" - and there were many different compiler implementations too.
It wasn't standardized until 1989 - 17 years later!
I think Rust will do just fine, and the compiler error message is (IMO) the best of any language, making adhoc exploration possible.
Can you imagine trying to explore C from ad-hoc alone based on its compiler errors?!
Sometimes doing a formal standardisation process too early can result in stifling innovation. I’m too young (at 36) to have insight in to the C development processes, but I suspect that having competing compilers trying out different things will have resulted in a better language in the long run.
It’s in many ways similar to the way the early web was developed, different browsers tried out many different additions, copied the better ones and improved them. The standardisation process we now have didn’t exist back then, and has been through a number of iterations itself since (the “xml every thing” process that ended with xhtml had a questionable outcome at best).
Some may argue that a formal standardisation process should have started earlier, before IE became too dominant. But I’m not too sure, IE was incredibly innovative and introduced some important technologies and apis, before stagnating.
In fact the “modern” html/css/js standardisation process is specifically lead by individual browsers implementing and trying out new additions, with formal acceptance dependent on other browsers also implementing it. It’s inherently a implementation lead standardisation process, not necessarily committee led.
>Sometimes doing a formal standardisation process too early can result in stifling innovation.
Realistically, I think the most likely outcome if there's a formal standard is similar to C: You end up with one compiler that adds extensions to the standard language that are too useful to ignore, and then ~everyone copies them anyway. The most useful extensions are then standardized in the next version of said standard.
Meanwhile, some poor suckers will be working in We Must Follow The Standards hellscapes and will have to stick to the ISO version, and they'll be completely left behind by the community.
The early web with every browser doing something else was terrible for website authors. A lot of the innovation in the web started when there finally was standardization so that these fancy features could actually be used reliably across browsers. You are representing that history in a completely wrong light here.
The web is a completely different beast though. You can reasonably target only one compiler, even if you lose some platform support. You cannot reasonably support only one browser.
The major difference between Rust and [Zig, C] is that the latter are essentially "cocky macro assemblers".
They derive their semantics from the semantics of the underlying assembly.
Take https://github.com/ziglang/zig/issues/12251 as an example. Several people from the Rust and Haskell community slacked off on twitter about how awful of a language design this is. However, when you think about what the language actually compiles to it makes perfect sense, and is pretty straightforward.
Rust has the goal of putting as much smartness between what you type and what the compiler produces, which is a perfectly fine goal, but without a specification for the input semantics it's a pretty wobbly thing, especially for a systems programming language. A lot of bit packing code is simply not writable in rust today without immediately invoking UB, that works for now but might break with every bugfix release.
> The major difference between Rust and [Zig, C] is that the latter are essentially "cocky macro assemblers". They derive their semantics from the semantics of the underlying assembly.
I don't know about Zig, but as for C, this take is not just wrong, but dangerously wrong. The behavior of C code isn't defined by the underlying assembly, but by an abstract machine model that may or may not match your real machine (narrator: It doesn't). As a result, the compiler can and will ignore the intention of your code when it can prove that your code would invoke UB. Good example: signed integer arithmetic and overflow tests. It is very difficult to write overflow tests for signed integer operations without accidentally invoking UB, and modern compilers will simply remove your overflow tests instead of translating them 1:1 to assembly instructions.
Assembly in the macro-assembler sense. Doesn't matter that it's obscure and not a direct mapping to any specific ISA, there is still relatively straightforward mapping between syntactic constructs and something von neumann-ish.
Rust has that mapping in the same sense that C does: there exists a fairly simple way to implement every language construct (except maybe for dynamic method invocations) in assembly.
However, Rust and C alike have another whole dimension to their semantics, that of Undefined Behavior, which is not reflected in the assembly, and which needs to be taken into account for unsafe code authors (in Rust) / by all programmers (in C). See for example https://www.ralfj.de/blog/2019/07/14/uninit.html for what goes wrong when you think of C as just a macro assembler.
The distintion between Rust and C looks interesting. Are there any formal (or formal-ish) grounds for saying that only authors of unsafe code need to be aware of Undefined Behaviour in Rust? With MiniRust, would it be plausible that some sort of "black-box abstraction" theorem could be proven, in a way that makes "safe" code depending on "unsafe" code insensitive to undefined behaviours?
Rusts entire trait model is a pretty big deal.
The main issue is with memory semantics however, stuff like tagged 64bit pointers are UB land pretty much immediately.
MiniRust solves this by providing an explicit memory interface trait.
> The main issue is with memory semantics however, stuff like tagged 64bit pointers are UB land pretty much immediately. MiniRust solves this by providing an explicit memory interface trait.
But this is no different from C! The way C programs interact with memory is defined in very similar terms to MiniRust's memory interface trait, using "objects" with some rules for which pointers are allowed to interact with which objects.
In both C and Rust, if you want to do pointer tagging or bit packing, you need to consider the language's rules carefully- this does not mean you cannot do it in either case, only that you are stepping close to the boundary of what is supported.
To be clear, MiniRust doesn't change any of the rules Rust has, it makes them precise. 64bit tagged pointers are possible in Rust, you just have to do it right, and ideally one day MiniRust will spell out precisely what "right" means.
(Maybe that's what you meant, it wasn't entirely clear.)
Yeah okay, traits are super complicated and C doesn't have anything like that, I admit. It's not really part of how to map instructions to the machine, it's more part of how to figure out which function is even being called at a particular call site.
If we understand "straightforward" in the sense that small changes in the input lead to predictable and small changes in the output, then I can't see how this is reasonably true for pretty much any optimizing compiler.
It's also worth noting that (at least for C), the way the standard is worded an optimizing compiler and a non-optimizing compiler can generate exactly the same output in every situation. NONE of the unpredictability actually depends on optimization, it's just that optimization can (sometimes, if you're lucky) expose it. If you're not lucky, the code gen changes based on something harder to control than a single compiler option.
Rust screams UB much quicker when it comes to memory semantics than any other language that I know.
Rusts lower bounds of code mangling are also much higher than in other languages.
I don't expect to have a good mental model of an optimising compiler in "gotta go fast"-mode, but a mental model of what the memory layout looks like is pretty darn relevant when designing or working with ABIs and binary data representations.
> I don't expect to have a good mental model of an optimising compiler in "gotta go fast"-mode, but a mental model of what the memory layout looks like is pretty darn relevant when designing or working with ABIs and binary data representations.
I'm not sure where you got the idea otherwise, but Rust supports the same memory layouts and ABIs as C on a given platform.
You don't get a specific layout by default because that allows for the implementation to improve over time, but that's not really relevant when working with binary data representations, where you simply specify which one to use.
My brain isn't cached into the issue anymore, because I've since solved the same problem with Zig, but about a year ago there was no way to have a reference counted tagged pointer that points to a boxed DST with the minimum number of bytes. Every path lead through UB or "not yet defined" somewhere.
You wrote a PHD on it so you might have a better idea of the issues that past me ran into, than what present me can still recall ;)
It might still be tricky or even impossible to do that, that sounds very specific. If it is truly impossible we in the UCG would love to hear that. :) We don't want to rule out any legitimate use-case without reasonable alternatives (and yes those are deliberately fuzzy terms), but we do want to guarantee some baseline of optimizations that almost all code can benefit from, and it is okay for us if niche code then becomes harder to write. If we accidentally make it impossible to write though, that would be an issue we'd love to know about.
I have it on my todo list to revisit the Rust implementation at some point.
Now that const generics have landed at least some parts of the code should be easier to write.
I'll throw you folks an issue over the fence, should I run into the same problems again, pinky swear ;)
UCG and their Zulip are a good place for thorny "is this UB" questions, yeah. We're thin on documentation indeed. First need to figure out all the rules, then find enough people to write all the educational material to teach them to everyone else. ;)
This is still wrong though, since UB doesn't exist at an assembly level, and yet C has it. Therefore there is no clear mapping from C to assembly (even considering hypothetical ISAs).
There are plenty of undefined things in ISAs, writing to some internal ARM registries also springs to mind, but that's a bit of a red herring. Just because code with undefined behaviour doesn't map to some abstract machine model nicely, doesn't imply that code that doesn't invoke UB can't have a simple mapping to some abstract machine semantics.
Yes, things like illegal opcodes, or other things the hardware _does_ but isn't officially part of the spec... those things are almost as old as computers. Certainly as old as home computers.
> Doesn't matter that it's obscure and not a direct mapping to any specific ISA, there is still relatively straightforward mapping between syntactic constructs and something von neumann-ish.
No there isn't. "Something von neumann-ish" would have behaviour for all inputs - maybe not desirable behaviour, maybe even different behaviour on different processor revisions, but it would have behaviour. The C abstract machine doesn't.
> Assembly in the macro-assembler sense. Doesn't matter that it's obscure and not a direct mapping to any specific ISA, there is still relatively straightforward mapping between syntactic constructs and something von neumann-ish.
That applies to pretty much all imperative languages that compile directly to assembly/machine code – including Rust.
C used to be a "cocky macroassembler", up until people started writing optimizing compilers for it.
If your compiler merely translates a source line into a series of assembly mnemonics, function calls, or interpreter gotos, then the interface is the implementation. You can rely on the underlying target language to provide your program with meaning and the only people who have to care are people reimplementing your compiler for compatibility.
The moment you start talking about optimization, then this no longer works. You no longer have a correspondence between source and compiled forms of one program. You have a many-to-many relationship where one source form can be compiled into hundreds of binaries depending on how the compiler is configured, and many source forms may actually optimize to the exact same compiled form. This requires you to provide your own semantics, else compiled programs have no meaning and -O3 becomes shorthand for "make demons fly out my nose".
In the case of C they came up with a series of rules for what-not-to-do that both did not match existing language semantics and also were dangerously incomplete. There are still C programmers who insist that you can free() memory but still touch it for a "little while"[0], or access memory "off the end" of an allocation[1], for example. And ISO C still made the mistake of retaining pointers, which are a confusing mix of value and reference type. They aren't references because you are allowed to cast them to and from integers; and they can't be values because you can use them to modify other values. Because of this tension, we keep discovering new combinations of valid transformations on valid programs that cause miscompiles, and then we have to invent things like pointer provenance to fix them.
As far as I'm concerned, the only difference between Rust and C is that Rust is honest about it's cleverness. C has to pretend to be simple while also out-clevering Rust (or at least, the safe subset of Rust).
[0] Usually in an attempt to emulate automatic memory management. Manual memory management does not work when passing complex structures across an API boundary, and the only options are to either expose custom deallocators (which means no optimizations even when they are sound), tell callers how to deallocate the data (which means no changing the data), or hack the allocator to do what you really want.
[1] It works for malware developers, it should work for me, right?
> they can't be values because you can use them to modify other values
That seems like an arbitrary criterion. They're values because they behave like values when copying them around etc. You can't even use them to modify other values implicitly - you still need to use * to get an actual dereferenceable/assignable lvalue out of the pointer.
What operator is used to modify the value doesn't matter, just the fact that you can do so. At the point where you use an assignable lvalue, the optimizer needs to know where that lvalue came from, which means that pointers need to be tracked like references.
My point is that because pointers act like both values and references, they are neither values nor references. This makes it impossible to soundly reason about them.
It is certainly possible to soundly reason about them. The C standard just does not describe how to do it.
There is plenty of PL work (in particular everything considering ML with imperative features, many decades worth of work) where references are values, that works perfectly fine.
Rust has no explicit goal to introduce magic - instead the goal is to be safe and sound. I'd argue the code very often compiles to what one might expect it to compile to, its certainly better than C++ when it comes to magic in terms of how useful and transparent said magic is.
There is a reason why rust has a datalog engine build into the compiler (https://github.com/rust-lang/datafrog). Which is imho totally rad and awesome, but really hard to fully form a mental model of without a spec.
This is pretty vague reasoning. Rust draws inspiration from each of those languages for something but that doesn't mean they affect its operational semantics, which are quite similar to C's.
The fact that you point to datafrog is illustrative: while it is not actually built into the compiler today, the use case for it is borrow checking, which famously does not impact the language's operational behavior at all! It is purely a compile-time analysis that does not influence code generation.
(For example, consider that mrustc and the GCC frontend are both able to omit borrow checking entirely and yet still produce runnable binaries!)
Of course, the borrow checker is still something that you need to build a mental model for, but by design it is okay for you to get that wrong occasionally, because the result can only ever be "your program still does what you thought, but the analysis proved that in a way you did not expect" or "your program does not compile."
I bet PL/I, Ada, and even Python 3 aren't far off.
Same applies to the Java and .NET ecosystem, because either you swim on the surface, or you really get to know how the implementations, down to bytecode, JIT, GC and standard libraries work, and now they are full speed ahead with 6 month release schedules.
after 5ys of (hobby but frequent) rust I'm pretty confident most people cannot tell what their code will compile to and what its runtime profile is going to be (aka you can't reason about performance -> so there is a lot of magic/complexity)
I've recently tried Zig and switched to it instantly, it's hard to explain but basically Andrew has a very good taste at picking important features and keeping the language complexity very low.
You know how it takes some time to learn borrow checker and macros and generics and traits and all weird rules of what you cannot do and then trait bounds and then it doesn't work exactly like you need, or the crate does not support something and you cannot implement it yourself, etc. etc.
So in Zig I had a hello world on day one, and the first thing I did was encode/decode ANY json messages for tagged union (which unfortunately is not supported in std but it was very easy to do it myself) and it worked! I did this the first day in entirely new language (I was not even doing C/C++ before) and it would probably take few days in rust and I'd probably mess up something and I know it wouldn't work for every case and every crate, because of orphan rule. In Zig it would work for any struct, internal or external. And also Debug, Display, Eq, Partial, all of that works automatically. That's huge!
And the worst thing is that recently, I've started using pointers again, and when I look at the code I don't see anything unsafe in the structure itself, it can be used unsafely but that is also very easy to fix in Zig because you have these explicit allocator and it's so easy to put everything in the same arena transparently, or use SegmentedList with stable pointers.
> "It really does feel like rustc is a big step toward the mythical Sufficiently Smart Compiler, letting you write nice code without worrying about a suite of low-level gotchas."
Don't take things to literally, the outcome of "we set out to build an arbitrarily smart compiler" and "we build an arbitrarily smart compiler to achieve X" are indistinguishable.
Neither has occurred and neither will occur. Again, stop making crap up. The goal of the Rust project is not to inject "as much smartness between what you type and what the compiler produces." That's a gross mischaracterization.
I'm just trying to correct disinformation. It's an important one too, because then otherwise you get to go on pretending as if the Rust project likes complexity for the sake of complexity.
> otherwise you get to go on pretending as if the Rust project likes complexity for the sake of complexity.
Your words, not mine.
I'm fine with a smart compiler. I also think that a high-level language (which Rust is in a sense) also really needs a spec, which MiniRust graciously provides.
This isn't about whether you or anyone else "likes" a smart compiler. This is about not making shit up about the goals that others have.
Maybe I should have said this before, but I'm part of the Rust project. I've been part of it almost a decade now. There has never been any moment or point in time in which I or anyone else I've seen in the project that has "the goal of putting as much smartness between what you type and what the compiler produces." You are just factually wrong.
If anything, it's exactly the opposite. You don't want too much smartness. But you need some of it (to the best of our knowledge) to achieve our actual goals, which, pithily stated, are "a memory safe systems language."
You shouldn't be giving Zig (or C, for that matter) a free pass. If your language isn't memory safe, and it compiles to LLVM with the optimizations on, then it's inheriting most of the complexity of Rust's undefined behavior. (I say "most" because I suspect that Zig doesn't use attributes like noalias as much, but there is still a lot of complexity around, for instance, provenance that are orthogonal to those attributes.) It's LLVM, not the language frontend, that actually implements most of the memory model (via lowering to machine instructions).
At about the time K&R1 was published, the Ritchie's original C compiler was being replaced by pcc, and `lint` was another early (diagnostics-only) implementation. The K&R1 reference manual includes a few mentions of features that were deprecated by pcc, or new in pcc.
So I think C was more mature than "the compiler is the specification" by the end of the 1970s.
I would say that, on the whole, doc.rust-lang.org serves a similar purpose to K&R. It's a comprehensive tutorial, a good guide for the standard library, and a good start for a reference but not strictly a complete definition of the language.
K&R is remarkable for fitting all that into one slim book, and Rust's documentation rightly doesn't try.
Rust's documentation also has some considerable advantages from its modernity. That library documentation gets to link to the actual source code which can answer tricky questions you'd never cover in a book, yet in many cases that source is very readable, its examples are something you can try for yourself with one click, instead of one of the very best human compiled indices of any book it has text search and hyperlinks.
Beware of reading the library sources though, they can change any time without warning. Only the docs are guarantees that remain stable as Rust gets updated.
> C was invented in 1972 and became one of the most widely used languages in the world with "the compiler is the specification" - and there were many different compiler implementations too.
> It wasn't standardized until 1989 - 17 years later!
> I think Rust will do just fine,
Not based on the example you gave - using the behaviour of a single C compiler as a specification for the language implemented by that fairly easy due to how small C is. Rust is very much larger than C.
> and the compiler error message is (IMO) the best of any language, making adhoc exploration possible.
> Can you imagine trying to explore C from ad-hoc alone based on its compiler errors?!
Not much different from Rust: Gcc and clang C compiler errors are exceptionally good, and I miss them terribly when programming in some other language (like C++, or Javascript).
There was the original K&R Book on C, though, the one which was about 100 pages, was super clear, and i believe gave compilers a common idea of what is normal.
Not sure I'd go that far. I am getting paid to work full-time in Rust right now, but... My experience is the # of companies doing this that aren't "web3" companies is pretty small. Insignificant as a % of the total industry. Rust forums on LinkedIn are dominated by chatter about Solana etc. When recruiters reach out to me about Rust, it's almost always some blockchainy thing. I wouldn't characterize that as mainstream.
Type "Rust" into a job search engine and see what comes up, it's mostly blockchainy stuff. Or a few shops that are "looking into" doing Rust for new projects (which is encouraging, but hard to say how that will shake out).
I personally won't touch anything "crypto", so I feel somewhat lucky to be getting paid to work in Rust right now for something that isn't. But I fully expect that once this gig finishes, it'll probably be back to C++ for me.
Also Apple, fwiw. I just left a job doing rust there a few months ago (for another job doing rust at another FAANG). But if you have an iphone and use icloud, rust code has probably been involved in some part of your daily life (just not on the phone itself) for the last couple of years.
Rust usage at these companies isn't yet 'large' in the sense of KLoC or anything, especially compared to existing legacy codebases, but it's getting put in at some really critical infrastructure levels that are unlikely to do anything but grow imo.
That's kind of the thing about rust though. That's where it's got the most power to improve things, so its introduction to a workspace is kind of subtle.
Oh that's interesting, I kept saying Apple is the only "big tech" company not using Rust. (Huawei should probably also be considered "big tech", but they are also using Rust.) But with Apple being as closed as they are, I guess it's not too surprising that one doesn't hear much about it...
Is there any public source for Apple using Rust?
And yeah those are exactly the domains Rust is designed for, and even if it doesn't mean huge codebases, that kind of usage at a whole bunch of huge companies hopefully means Rust is more likely to survive beyond the initial hype. :)
That particular posting has been up in some form forever. I'm always tempted to apply, since I'm from Edmonton area originally, and my whole family is there, and I do Rust. I've always found it curious that that posting exists, since Apple has no other presence there.
But a) it's Apple, a stressy, workaholic company to work for [at least that's how it seemed when my wife worked there] and b) I'd never convince the wife and kids to move right now.
It's the team I worked on. Apple has had an office in the Edmonton area for about 9 years now, originally in camrose (which if you're from here you'll probably think is hilarious), now in downtown Edmonton (the move was during COVID lockdown). It's generally been about a 10 person team, give or take, but recently split into two teams that are both doing rust in different areas.
It's a surprising location and the story behind it existing is fun but also not mine to tell, but if you applied and interviewed you'd almost certainly get to hear it. The stuff that team does is much more important than its location would suggest and the software they build was mostly originally in C (not c++), which is also kind of surprising for the services side of apple. We were involved in porting swift to Linux in the hopes it would meet our needs but that didn't work out for various silly reasons.
At any rate it's not the only part of apple using rust, but it is probably the earliest adopter and also the most mission critical user there.
> But a) it's Apple, a stressy, workaholic company to work for [at least that's how it seemed when my wife worked there] and b) I'd never convince the wife and kids to move right now.
Fwiw this team isn't really any of those things, it's very unusual in a lot of ways. Very good work life balance, especially now that the office isn't an hour from a real city.
I didn't leave on bad terms at all, I just needed a change of scenery and the one big downside to that office is you can't really go to any other teams because Apple is so dead set against remote or distributed teams without a shared office space, so you're just kinda stuck. Also I ran out my patience for not being able to work on open source unrelated to my job (though at least it got easier to work on open source related to my job over the time I did work there).
I looked around for work in Rust inside Google a year or so before I left and it was only some small parts of Fuchsia and not anything hiring at my site.
But I'm sure it's growing. But it won't be in the core Google3-on-Borg parts of Google, which are on the whole in either C++ or (increasingly) Go. Switching to a standardized garbage collected Google supported language makes sense in that domain. The "Carbon" stuff recently announced sounds interesting as well.
The long term looks good for Rust. But actual production code is still thin on the ground still everywhere.
(disclaimer: I work on Fuchsia, not speaking on behalf of my employer, feel free to clone the source repo and make your own analysis)
FWIW, to the best of my memory as of a year ago I counted ~1.1 MLoC of Rust code in our open source tree, with an additional ~1.3 MLoC in things we've vendored from crates.io.
There was a bit more C++ when I did the analysis, but it was the same order of magnitude.
Good to hear. A few years ago, there was a team advertising on grow/ for Fuchsia & Rust (for networking stuff I think), and they advertised in Waterloo but when I inquired, the posting went away and I was told they weren't actually hiring at my site. shrug Maybe it was my bad breath.
Other than that... Fuchsia was the thing that was rewriting all my work from scratch and killing everything I worked on (Chromecast on Home Hub, etc), so... :-)
> My experience is the # of companies doing this that aren't "web3" companies is pretty small.
Those are just the loudest people. Web3 companies depend on hype so they shout all over the web about anything they can, including how they're using the hot new language for blah blah blah and you can come and write it with us! (As long as you're willing to be paid in worthless tokens.)
In normal companies there is plenty of Rust use but you don't hear about it as much because it tends to be initiated internally by people who weren't hired as Rust programmers, and it's used for new projects that take a long time to get to the point of needing new employees. I know in my company it was at least 2 years between when we started using Rust and when we wrote "Rust" on a job advert.
It really would be, and we need to fight that. It's definitely a trend I've been seeing and it worries me.
Do a search on Upwork for Rust gigs. Almost all crypto.
LinkedIn jobs, very similar. Putting "Rust" in my profile there has also led to a lot of recruiters pinging me about web3-ish jobs that I have no interest in.
The Rust PL group I'm on, also on LinkedIn, has a steady stream of 'smart contract' etc discussions. And people advertising themselves for crypto jobs. Or crypto jobs advertising looking for people.
That's not to say there isn't plenty of other Rust stuff out there, but it seems like this is where a lot of the use is right now and I really hope the language as a whole doesn't get typecast this way.
I got the same impression looking for Go a few years ago. I guess in a world without legacy code because it's too new for legacy code, you're going to use the newest programming languages. That was also the kind of startup that was getting funded back then. I haven't paid much attention but maybe the hype has died down over the last few months.
My overall thought is that programming language popularity is a lagging indicator; people might be hiring Java engineers even if they wish they didn't write their codebase in Java. Changing that is risky; getting one more employee can stop the bleeding. Meanwhile newly-formed companies use the newest tools, and thus among newly-formed companies, you're going to see many more requests for engineers with experience with the newer tools. (It's one of those secret advantages for startups; your competitor might be slowed down by all the legacy and thus resources given to your new startup might get more value than if given to the incumbent.)
> belonging to or characteristic of the ideas, attitudes, or activities that are regarded as normal or conventional, or the dominant trend in opinion, fashion, or the arts
by dictionary definition, rust is not mainstream. it's not normal (common, typical, customary) for an engineer to interact with rust
I would consider it perfectly normal to discover something in my company or on Github was written in Rust. It's the 12th most popular language on Github by PRs according to this: https://madnight.github.io/githut/#/pull_requests/2022/1 (Shell and Nix don't count).
It's only 3 times less popular than C. As I said, very weird definition of mainstream.
The measure of "mainstream" should not be "projects on github" but: code deployed (or actively deploying) in production. GitHub is full of personal interest and research and unfinished projects.
It will come. It's not there yet.
I still argue it's not the language best suited for what a lot of people are trying to use it for. It has a niche, one that I like, but I fail to understand why it's being deployed for web services type applications. IMHO it's not a good fit.
> I fail to understand why it's being deployed for web services type applications. IMHO it's not a good fit.
There are plenty of reasons why it's a good fit for web services. It's fast, has great tooling, a great library ecosystem, and perhaps most importantly the strong type system and borrow checker mean you write far fewer bugs than in most other languages (C++, Java, Typescript, Go etc; maybe not Haskell).
The only reason I wouldn't use it in web services is because the whole ecosystem seems to have decided that everything has to be async despite that adding a ton of complexity and almost nobody needing it.
I think people are seduced by some of the modern language features (like pattern matching, etc) by some of the third party libraries, by async [careful!], and primarily also by the WASM story that seems to be kind of going along with Rust. And by the novelty. And by promise of vaguely defined "performance."
But none of this is unique to Rust. And I'm not convinced on the WASM stuff at all for writing web front ends, though I'm enamoured of it as a generic backend managed VM, but that's another story.
And I think adopting Rust in many shops with a mix of developer seniority levels will be a productivity killer. I think most places doing this kind of thing would probably be better off with TypeScript, or Kotlin. Or if they want to go more functional and exotic, F#, Scala, OCaml, or Erlang/Elixir, etc.
But for places where we've been doing C++ for years and setting off footguns all over the place? Rust is great! Love it.
If Rust leads to more languages for more domains adopting Rust-style enums and pattern matching (algebraic data types / sum types -- this idea predates Rust by decades, so "modern" is an interesting term to use), I'd call that a big win for everyone. :)
It's pretty tricky to do that in an untyped language. But TypeScript could have this, or maybe even already does?
I mean, sure, why are we calling it "Rust-style"; it comes from ML and its descendants and Rust is obviously very influenced by them. When I say "modern" I really mean... finally learned from what "we" were "all" reading about on Lambda The Ultimate 20 years ago about languages designed 40 years ago that were not part of the mainstream until about 5-10 years ago :-)
I used "Rust-style enums" because if I just say "enum" then people think I mean something else. ;) And then I added the clarification to make sure nobody thinks I would claim that Rust invented Rust-style enums.
If I just say "algebraic data types" or "sum types" I fear many people won't know what I mean...
I think "discriminated union" is a useful bridge term here, even if it does confuse C programmers due to the conflated meanings of both enum and union in that language. It better represents the concept at an implementation level, and maps it to the C structure that enums more closely resemble in practice.
From a quick scroll, that seems to be just C-style `select` with `if` guards?
What I was referring to is things like `match x { Some(y) => ..., None => ... }`, where there is data that is available only in some variants of the type (like `y`). Without a type, even naming these variants becomes an interesting problem. (Not unsolvable, mind you. But not obvious either, and you lose some of the benefits that algebraic data types have in typed languages.)
That’s not a metric I would start for defining mainstreamness. There are at least 2 orders of magnitude more program written in C that is not open-source, but part of a critical infrastructure.
Yet here you are on a discussion about it, of many rust discussions. Many companies use it enough that most of the MANGA companies have openings for it.
How is that not mainstream by even your own definition? It’s consistently a part of the current zeitgeist.
And not only that, specification is an explicit goal (they have a sepcification person in the core team even at this early stage).
But what is interesting I think is that if miniRust is successful (or even if it isn't) zig can learn lessons on how to do borrow checking on zir: even if it isn't part of the official language release it could be a useful tool for ci/linting/touchup, especially if it becomes an "anointed" tool
Wrong. There is a clear set of logical rules by which you can look at code and reason about whether or not there are resource errors. If you can do it, a computer can do it too. You may need to have hints, like annotating a function called "alloc" or "open" as creating a resource, but I think that's not unreasonable.
Yes, that "clear set of logical rules" is called the Rust ownership and lifetime system, and the system that enforces it is called the borrow check. And, having designed a lot of Rust's borrow rules, I'm telling you that the borrow check only works because of the specific rules that Rust enforces: in particular, no more than one mutable pointer to a location. Non-Rust languages don't enforce those rules, so grafting the Rust borrow check onto the language will rule out a lot of idiomatic code. For example, in C++, you would be unable to write shared_ptr<T> for non-const T (this is just one of a mountain of examples), and I'm sure that the situation would be similar in Zig once you start diving into the details.
I'm much more interested in specific details of how to permit mutable aliasing while simultaneously avoiding use-after-free. From what I've seen, the only compelling answer is a garbage collector.
I think your concept of what is possible with languages is limited and you are suffering from either narrow tunnel vision or some sort of emotional overinvestment in what you've put effort into. I don't know what the right answer is. It could be as complicated as something like SEL4 does, it could be like typescript is for JavaScript, it could be annotation based like dialyzer is for Erlang. Not all of these are perfect, of course, but the point is is a wide open space of tradeoffs in safety, performance, and ergonomics (including ability to reason about scary vulnerabilities that rust can't auto fix like race conditions or business logic errord) between "what rust looks like" and "c level wild west".
So far no language not designed for it has demonstrated that adopting a borrow-checker to interesting real-world use-cases is possible. The C++ project (C++ Core Guidelines it is called IIRC) eventually gave up on soundness. Zig isn't there yet, I am very curious how they will tackle this.
So, I'm not going to use the word "impossible", but I think this is a hard problem and many people are underestimating how hard it is. It is strictly harder than what Rust is attempting, and see all the tricks they had to pull!
Put differently, if you are right, it should be easy to prove pcwalton wrong by just doing it. :)
I mean, I don't think they were claiming the rust way is the only possible form, but rather that any solution as expressive and safe as the rust method will be about as complicated, which doesn't seem to disagree all that much with what you are saying to be honest.
The interesting question is whether there exists some system fundamentally/significantly simpler than the rust approach which still gives you comparable freedom and safety (without excessive runtime overhead).
As far as I know that is unsolved and a matter for research, not some obvious solution that the rust team totally missed when designing their language.
If your goal is lower than total memory safety, then obviously you'll have more options, but when it comes to memory models, you have to be quite careful in where you allow violations, since they tend to infect everything quickly if you don't find exactly the right way to encapsulate them, so having a "half-safe" language often just means you have an unsafe language. Part of the reason unsafe rust is so painful is they're adding exactly that sort of hole in the memory model, and suddenly anyone exposed to it can't deal with the "simple" memory model of rust, they have to deal with all the gory details _creating_ that simple model. Any language adding larger holes than unsafe will need more people to be aware of the full complexity of modern compilation more of the time, which doesn't seem like a win. Rust's unsafe works because it's almost never used. If unsafe was half the codebase, rust would be a failed language.
Another solution is to absolutely refuse to take advantage of your safer memory model in the compiler - so minimal optimizations, just compile literally and throw barriers all over the code, then anyone using a downgraded model doesn't need to know the additional rules they would otherwise need to follow to interact with the full model. That sounds pretty awful though, probably more expensive than just using a garbage collector.
Is there a better solution? Sure, probably, but I don't think humanity has discovered it yet.
The real problem is pretty much any model that doesn't shoot for complete memory safety (or a unsafety that is strongly discouraged and well encapsulated) is going to end up containing all the complexity that comes from unsafety, rust justifies its complexity by all the complexity it also removed (undefined behaviour is basically not a thing in Rust), if you half the complexity compared to rust but retain half the undefined behaviour complexity of C++, I think you'll have just grafted the worst of both together. I'm unconvinced there's a sweet spot in the middle, more like a mountain of extreme complexity (ala rust unsafe) between 2 valleys of relative simplicity (C-like, rust-like).
To back up the comment on specification. On one of the recent Rust podcasts, they had Andrew Kelley on. From what he says, the roadmap has them publishing a formal specification of the language once they 1.0. So yea, it is on the roadmap and an intention with making the stable 1.0.
It's still a good example where the specification is in flux and a lot of documentation virtually non-existent (e.g. std), but that is still very usable, simply due to the fact that it isn't a very complex language in terms of semantics, which in turn makes it less dependent on a very precise spec.
Anyone able to explain to me the significance of this? Read the article and ReadME but I don't understand its use case particularly well or why this has shot to the top of the front page
This is one small piece of the work that's going to need doing to get Rust properly described.
It's not "useful" in the sense of being something that you'd actually run.
This is a start on the lowest layer of the description, describing the "abstract machine" that runs Rust programs; it assumes that all borrow-checking and type-checking has already been done, generics have been expanded, and so on.
The other two big pieces that will need doing are properly describing the type system, and properly describing how the high-level features that aren't strictly necessary can be "lowered" into simpler forms.
> It's not "useful" in the sense of being something that you'd actually run.
What if we could actually run it (stay with me for a second) with a well known interpreter such as python3 or v8?
That is the direction py2many has taken. Trying to define a small subset of python + missing rust features (pattern matching as an expression), interfacing with formal verification methods such as z3 and then finally building a transpiler to transpile this language to rust.
My goal is that we can actually run it, but that would still only be useful for the following things:
- Testing the spec itself
- Ensuring that a more production-grade interpreter like Miri has the same semantics as the spec it claims to implement
It's not intended that unsafe Rust authors would ever run the MiniRust interpreter; if they want something like that, they should run Miri instead. They might still read the MiniRust interpreter sources to figure out what the heck Miri is doing.
If you are writing unsafe Rust code and want to be sure that your code is following the rules of the language, then this is an important steps towards giving you a sufficiently precise description of those rules that you can use for this purpose.
If you are writing a Rust compiler or a Rust verification tool, then a spec like MiniRust is a key part of telling you whether you got it right.
If you are not doing either of these things then you probably don't have a use-case for MiniRust. :)
My first thought when I saw MiniRust was "cool, a way to get the binaries small enough for embedded" but what I got was actually way cooler. Ralf is a great writer, even though I'm too slow for Rust he's got a talent of making all the arcane concepts seem accessible.
> "cool, a way to get the binaries small enough for embedded"
lol, I didn't even realize the wrong associations I would create here. Glad you liked it anyway. :)
I guess my choice of name was not great. I also considered pseudo-MIR but that didn't really capture it, either. And "formally specified fragment of Rust" isn't very catchy.^^ "CoreRust", maybe it should have been "CoreRust"...
Using "mini" to mean a subset of the language rather than a version for small systems has precedent. For example in the Perl community, miniperl is a subset of Perl. It's mostly used to bootstrap builds of the full language, but in theory can be used separately as a restricted programming language. It's also the name of a module, ExtUtils::Miniperl, for Perl (https://metacpan.org/pod/ExtUtils::Miniperl) that builds miniperlmain.c and perlmain.c files to bootstrap the compilation of the language system. This is not to be confused with the Raku project on Github called "miniPerl" (https://github.com/grondilu/miniPerl) which compiles subsets of Perl via the Lambda calculus to JavaScript output.
I'd personally pretty much always expect "mini" or "r" (as in "rperl", a restricted subset of Perl with C++ connections) versions of a language to be restricted subsets for some purpose (rperl's is to give away flexibility for performance while maintaining a good portion of the original language).
I've seen an "e" or "emb" prefix or a "small", "tiny", "micro" or "µ" (or "u") prefix to mean a small toolchain version several places, like SmallC or uclibc or Mikroe's mikroC (https://www.mikroe.com/mikroc). It wouldn't surprise me to see a "nano" version of a language tool either. Sometimes these are subsets as well, but to fit the size constraints of the target rather than for constraining the input for its own sake.
I guess you could argue that the Raku Programming Language also has its "mini" version, to build the actual language on. In Raku's case, it's called NQP (which used to stand for "Not Quite Perl", but now is just considered to be "NQP").
Programming in NQP is troublesome, but it is fast and it is self-hosting.
The two projects are related, but have different objectives (mir-formality includes traits and borrow checking, while MiniRust focuses on operational semantics).
Yes, MiniRust is basically a slightly extended MIR. But MIR is designed for borrow checking and MiniRust is designed for exploring semantics. And MIR doesn't have a precise operational semantics so it's not like this is duplicating much if anything.
>Is that someone the cool bear making an appearance on my blog? We’ll never know… and also I’d have to ask fasterthanlime to ask them for permission and didn’t plan this well enough.
Cool bear has breached containment.
I do have to admit I love the recent-ish trend of including dialogues in blog posts. Plato would be proud.
Cool Bear has been making guest appearances in a couple places already! I do not intend to pay lawyers to pursue anyone who uses them without permission but I appreciate being asked first — it lets me keep track of where Bear travels.
My only worry regarding Cool Bear is the licensing of the two pieces of artwork: I bought them a while ago on some stock image website but the terms weren't super clear, so I might need to revisit that. Since then I've commissioned drawings of bear and myself (5 variants each) but I haven't had a chance to use those yet — they're not monochrome, which makes dark mode awkward.
I don't have a good way to include the image anyway (or I am too lazy to figure out how to do that in Jekyll, I guess), so its appearance(s) on my blog will be text-only for now. Thus no image license trouble. ;)
I wonder how this fits in with the whole trademark "panic"... [1] but considering they're part of the rust-lang org on GitHub, I'm guessing that issue would have been brought up by now.
Ralf is the head of the official Rust team tasked with specifying Rust's operational semantics, so as far as trademark usage this is approximately as official as it gets (keeping in mind that the project in the OP is still just an experiment, and not any sort of stable or normative document).
It's certainly not official, so please don't take all the choices MiniRust makes as being anyone's opinion but mine -- but we're working towards having something official like it. :)
"MiniRust is the cornerstone of my vision for a normative specification of Rust semantics. It is an idealized MIR-like language with the purpose of serving as a "core language" of Rust. This is part of a larger story whose goal is to precisely specify the operational behavior of Rust, i.e., the possible behaviors that a Rust program might have when being executed: the behavior of a Rust program is defined by first translating it to MiniRust (which is outside the scope of this repository), and then considering the possible behaviors of the MiniRust program as specified in this document. That translation does a lot of work; for example, traits and pattern matching are basically gone on the level of MiniRust. On the other hand, MiniRust is concerned a lot with details such as the exact evaluation order, data representations, and precisely what is and is not Undefined Behavior."
It is yet another step on the path to having a Rust specification, especially with respect to precisely specifying the capabilities and requirements of `unsafe` code.