Since a major thrust of the linked article is barriers to rust adoption it seemed relevant.
Correct.
I think it’s a symptom of there not being enough places where adherents of different programming ecosystems can argue back and forth about our choices. I think there’s an unmet need in our community to have those conversations, and there’s probably something healthy in that.
It definitely distracts from the specifics of the article though. The corresponding discussion on reddit is much more technical:
https://reddit.com/r/rust/comments/10ld2vn/blog_post_next_ru...
But the compiler is really slow. Even an incremental build on a mid size project is never below 10s, whereas on a similar size project, Golang will take me less than 1 second to build incrementally. Also, I find cross-compilation (from/to major platforms) really challenging on Rust, as opposed to Golang where it's for my use cases super straightforward
What I want is a Rust, but with almost everything stripped away. Complexity similar to Go.
ocaml has potential but I strongly dislike the numeric operators, and I'm not a huge fan of the syntax in general.
OCaml with python like syntax would be damn near perfect. I could even overlook the operator thing.
I also want a more lightweight syntax but beggars can't be choosers.
It's like saying "I would like to have a non-volatile storage device that is as long-lasting as microfilm, as fast as an SSD and as cheap as a HDD." At some point, you have to renege on at least one of your constraints.
This is not to deny that Rust also has other sources of complexity. But, most of Rust’s complexity can be traced to its fundamental goals of safety and performance. If you’re willing to sacrifice one of those, there are much simpler and easier languages to use.
it's true golang is smoother, but the language is a lot messier (idioms and typechecking).
Can't conclude anything yet, but it makes me think back of rust more often than others.
I do 100% agree on your commends on cross compiling though. I also don't like how the target for linux has "unknown" in it.
For me the biggest hurdle to adopting Rust is that rust-analyser turns my laptop's fans up to 11. I don't know whose problem that is, but it doesn't happen with other languages, and my black box impression of rust's compilation infrastructure is that it's fucked up.
It’s just tricky because writing a new rust compiler is a monster amount of work because of how complex Rust is. And rearchitecting the existing compiler would also be a massive undertaking because there’s so much existing code, and it’s spread over rustc (written in rust) and llvm (in C++).
A faster compiler probably wouldn’t bother with all of llvm’s intermediate representations. Refactoring across a code boundary that spans two languages (and two projects) sounds utterly exhausting.
Rust is the first language in its category. It won't be the best language we ever make in that category.
Edit: trait-based OO is a breath of fresh air compared to tree-style class inheritance. Super flexible without having to overthink.
Immutable by default is reassuring, Option/Result are fantastic null/exception replacements.
Enums and match blocks are powerful and gracefully help ensure handling of all cases, have nice syntax, and work well for a systems language.
I programmed Ruby for years before learning Rust, and aspects of Rust's library and language inspiration from Ruby are obvious to me: the closure syntax closely resembles Ruby's blocks, for example, and much of the core iterator APIs match their Ruby counterparts. It's a very different language overall, of course!
(Then of course there's the part where many early Rust contributors came from the Ruby community.)
If you think strong typing is "ceremony" then you probably spend most of your time writing (and documenting) code and not reading, refactoring, or collaborating on it.
The time people spend writing unnecessary, buggy unit tests (that static analysis can do in better languages) is far greater than the time to just use the type system.
Most languages don't even force you to be explicit anymore. They infer the types, so you don't even write extra code. You just get better errors and speed.
JS/TS just requires fewer decisions per line of code. As a pithy example, in javascript I don’t have to decide whether I want a String / &str / SmartString / Rc<String>, etc. It’s just string. When I pass a callback I don’t have to decide between accepting a closure or FnOnce/Fn/FnMut. Or decide whether to accept a function pointer or take a generic parameter. In javascript I never have to think about the lifetime of the callee’s stack.
I tried to implement my own server-sent events style protocol in rust a couple years ago. I spent 2 weeks trying a bunch of different approaches but I eventually gave up - I just couldn’t get it working. (Mind you, async was pretty new then - I don’t think it was ready). I moved to javascript and had the whole thing working correctly in about a day and ~50 lines of simple code.
I think the resulting program is much better when I write it in rust than what I get when I build on top of nodejs. Rust programs can be orders of magnitude faster and I have a shockingly low defect rate in shipped rust code. But there’s a trade off. Rust programs take more effort to write.
Rust is a brilliant systems programming language. I’d pick it over C or C++ any day of the week. But not all problems are systems programming problems.
Never mind that some of these choices you can just avoid by thinking in general terms and deferring optimization for later. Just passing String and FnMut everywhere will be a lot more efficient than whatever the JS translates to on the machine.
> Just passing String and FnMut everywhere will be a lot more efficient than whatever the JS translates to on the machine.
I can’t comment on the performance of FnMut, but I’ve seen rust code run slower than the equivalent javascript because the rust code in question allocates everywhere without thinking about it (via Vec, String and Box).
In my mind, javascript and friends are good languages to build things fast. Rust is a good language to build things right. For plenty of software (eg websites), good enough is good enough.
Absolutely. Thank you for speaking truth.
Lifetime annotations help how you use and exchange certain data, but you can get away without them unless you need them. For shared data structures (when synchronization primitives are too much) and highly performant code.
You can write high level Java, Ruby, and Python code in Rust if you want to. Writing Actix or Axum web handlers feels no different than Golang or Python/Flask.
let mut v = Vec::new();
v.push(m_foo);
In fact it's so nice that you often rewrite code to keep clear what's actually happening, even though you could keep types largely implicit.Here's another example:
let f: Vec<i32> = vec![1, 2];
let f2: HashSet<f32> = f.iter().map(|n|n.into()).collect();I'm not sure what it is - there just always seem to be random symbols that dont seem to follow conventions of other languages. Guess I am use to C-style (including C++, C#, JS) syntax and python. Its possible that there are just new concepts that aren't really represented in the languages I use too.
Again, I don't necessarily think its bad. Just what I don't find intuitive.
What you're actually seeing is a trap a lot of programmers fall into where if something is unfamiliar then it's treated as if it's incorrect.
I said that I don't think it's bad or wrong.
One of the hopes is that it's usually not necessary to write those lifetimes explicitly in most programs, unless you're doing something unusual.
If you could go back and change it, what would you have used for the lifetime syntax?
&'a X
looks less like Perl / line noise than this:
&@a X
. In any case, not something we'd be likely to change at this point, but I agree that ' is effectively "arbitrary unique symbol with no evocative meaning".
(Arguably the same is true for things like & for "address of", but that has a long history in many languages which makes it more intuitive for current developers.)
&x\a
The current tick is much easier on the eyes.Other than 'static, a Rust beginner is unlikely to run into any lifetime with a meaningful name for quite a while. If when learning a new language you only ever saw variables named a, x, t, s, v and m it's not a big jump to guess that p, z and b would be allowed as well, but would you assume that in fact the language allows big_set and old_corp_logo as identifiers and it just forgot to mention that ? I'm not sure I would.
The Rust standard library does have lifetimes with reasonable names, for example std::thread::Scope needs two lifetimes and it names them 'scope and 'env which, although brief, are clearly not single letters. But most of the library and documentation doesn't bother with meaningful names, since it doesn't have anything worth saying about the lifetime, e.g. the signature of str::ends_with:
pub fn ends_with<'a, P>(&'a self, pat: P) -> bool
where P: Pattern<'a>,
<P as Pattern<'a>>::Searcher: ReverseSearcher<'a>I think it'd be a really good idea for the standard library to change many of its lifetimes to be descriptive, to encourage others to do the same. With my libs-api team hat on, I'd happily merge PRs that do that. (In small batches, please, not the whole library at once.)
It's the same syntax in Lisp, Haskell, Ocaml, Ada and VHDL.
I've put together a playground at https://play.rust-lang.org/?version=stable&mode=debug&editio.... It's not just dereferencing invalid */& that's illegal in Rust, but constructing and dereferencing valid &/&mut in ways that don't respect tree-shaped mutability. Rust's pointer aliasing rules invalidate otherwise-correct code, placing roadblocks in the way of writing correct code. There's so much creation of &mut (which invalidates aliasing pointers for the duration of the &mut, and invalidates sibling &mut and all pointers constructed from them), that's so implicit I don't know what's legal and what's not by auditing code. (Box<T> used to also invalidate aliasing pointers, but this may be changed. The current plan for enabling self-reference is Pin<&mut T>, but the exact semantics for how and when putting a &mut T in a wrapper struct makes it not invalidate self-reference and incoming pointers, is still not specified.)
(I've elaborated further at https://news.ycombinator.com/item?id=33658253.)
But it leaves that work to the developer, thousands of hours of testing and reviewing to ensure no little corner case is missed.
I think the argument that unsafe is used making the language have “holes” is somewhat misdirected. When I see a rust implementation of a doubly linked list with unsafe I know exactly where I’m on my own (a few lines) and where the compiler does the job for me (the rest of it). It’s not as if that means safety or flexibility is out the window. It means “do the manual safety review on these two lines”.
That's not true, because you can wrap unsafe code in safe interfaces and use it from safe code.
With C, you can only do these kinds of safety guarantees with a lot of discipline. With Rust, you can offload much of your discipline to the compiler.
For example you might build an abstraction named ReadWriteMutex that guarantees safe access at runtime.
I don't see a scenario where this would impede performances. The invariants to validate are the same. But rust would guarantee that it is memory safe.
If you do need to do something this exotic, the fundamentals don't change: you still need to take care of memory lifetimes, you still need to ensure thread safety, etc. I really don't think it's as all-or-nothing as you believe it to be.
I hear what you’re saying, but that hasn’t been my experience with rust. I love C, but I’ve been writing rust for the last couple of years. And rust has stolen my heart.
You’re right that unsafe rust is a bit less ergonomic than just writing C. Sometimes I miss void pointers. I definitely miss how fast C compiles. But most rust isn’t unsafe rust. Even deep in my custom in-memory btree implementation I think more than half of my methods are safe. And safe rust is a fabulous language when you can use it. There’s all these bugs you just can’t write.
Early on with rust I had this magical experience. My program segfaulted in one of my tests because of memory corruption. It was “spooky action at a distance” where the segfault happened well after the buggy code was executed. Bugs like this are awful to track down in C because the bug could be literally anywhere. But when I looked at the trace, there was only one unsafe function which could be causing the problem. (Since I could rule out all my safe code). Sure enough, 10 minutes later I had a fix.
I have a skip list for large strings that I ported from C to rust. I still have no idea why, but the rust code ran about 20% faster out of the box than the original optimized C code. And yet, the code is significantly smaller and easier to work with. I’ve added a few more optimizations since then - it’s about 10x faster than the C code now. The new optimizations are all from tricks I never got around to adding in C because I was afraid I might break something.
And then there’s the things rust does well that aren’t in C at all. Cargo is incredible. Monomorphization is the right tool for a lot of problems, like custom collections. (Higher performance and types? Yes please!) Parametric enums & match statements are so much better to work with than C enums and unions. Then there's rust's std, which is fantastic. I use Option, Result, Vec, BtreeMap, PriorityQueue, the OS-agnostic filesystem API, and so on daily.
So yeah, I hear you about C being lovely. But I think rust is even better. Rust is a bit of an ordeal to learn but I'm really glad I learned it. Its a joy to use.
IMO the market share for this profile is indeed small. Languages that trade off a part of that security for simplicity may have considerably more market share in the future, although on the other hand, the public may instead prefer a "fast-enough and dead-simple" language.
Can you give some examples of this? If there are ways we could simplify the language without hurting existing use cases, we should consider doing so.
The good thing about the default hello world example is that it introduces you to both language features without being overwhelming (IMO).
You can read more about format strings here: https://doc.rust-lang.org/std/fmt/
It would be more impressive if Rust had an actual type checked variadic print format function like C++ 23's std::println - nobody should be under any false impression about how impressive that feature is, but the type safe macro is effective, that'll do pig.
AFAIK C++ does format string parsing at runtime (as you need to provide a parser function), but maybe it can be made constexpr? I've not written C++ for over a year now so my fluency is fleeting a bit