> But I do not think it is a good general purpose language.
Remember that this is not a sentiment that's shared by everyone. I use Rust for tasks that need anything more complicated than a shell script. Even my window manager is controlled from a Rust program. I say this as someone who has been programming in Python for nearly two decades now. At this point, I'm about as fast in Rust as I am in Python.
While the Rust devs believe such cyclic references are rare - I think this speaks mostly to the problem domain they are focused on. Relational models are everywhere in Apps, they are often common in complex systems software like databases, and they are fairly rare in firmware/drivers/system code code.
There are a few patterns for dealing with cyclic references, but they all end up requiring either unsafe or a main "owner" object which you clean up occassionally (effectively arena allocation). Having now worked in C/CPP - the idea of having unsafe blocks sprinkled around the code doesn't bother me, and many C/CPP components have some form of arena allocation built-in. I just wish Rust learning resources would be more upfront about this.
It's not like that you can't write relational models in the safe Rust. The only forbidden thing is a reference pointing arbitrary memory, which is typically worked around via indices and often more performant in that way. It is much rarer to find applications that need an absolutely random pointer that can't be hidden in abstractions in my opinion.
While beginner resources don't dwell too much upon cyclic references, they don't consider unsafe blocks as unusual. All the material I've seen say that there are certain domains where Rust's compile-time safety model simply won't work. What Rust allows you to do instead, is to limit the scope of unsafe blocks. However, the beginner material often won't give you too much details on how to analyze and decide on these compromises.
Anyway, compile-time safety checks (using borrow checker) and manual safety checks (using unsafe) aren't the only way to deal with safety. Cyclic references can be dealt with runtime safety checks too - like Rc and Weak.
Indeed. Starting out with code sprinkled with Rc, Weak, RefCell, etc is perfectly fine and performance will probably not be worse than in any other safe languages. And if you do this, Rust is pretty close to those languages in ease of use for what are otherwise complex topics in Rust.
A good reference for different approaches is Learn Rust With Entirely Too Many Linked Lists https://rust-unofficial.github.io/too-many-lists/
It is no accident that while Val/Hylo, Chapel and Swift have taken inspiration from Rust, they have decided to not inflict the affine types directly into the language users, rather let the compiler do part of the work itself.
a doubly linked list
Parent field is something where you have a clear hierarchy (it's not really “cyclic”, so it's the perfect use-case for weak references).
When coming from a managed-memory language, this obviously requires some conceptual effort to understand why this is a problem at all and how to deal with it, but when compared to C or C++, the situation is much better in Rust.
It's very noticeable how often people who "need" a linked list actually only wanted a queue (thus Rust's VecDeque) or even a growable array (ie Vec).
Aria has a long list of excuses people offer for their linked lists, as well as her discussion of the time before Rust 1.0 when she sent lots of Rust's standard library collections to that farm up-state but wasn't able to send LinkedList.
I've still been meaning to write something in / learn Rust's ways of thinking; is there not an intended replacement for these data structures? Or do they expect it all to go under Unsafe?
For linked-lists, there's one in std and the majority of people should never have to write their own as it's error prone and requires unsafe.
For graph use-case then you can either use ECS, arena, ref counting or unsafe, but you're probably better off using/developing a dedicated crate that optimizes it and abstract it away behind an easy to use (and safe) interface.
Sometimes such a primitive doesn't exist and you should use unsafe yourself, but then you're the one supposed to make sure that your code is in fact sound. If you keep unsafe for small portions of the code you can reason about and extensively test so Miri gives you a good level of confidence, then it's fine to use unsafe. But it's the more expensive option, not the default.
I just put up a blog post about this actually :) https://jacko.io/object_soup.html
> promoting the shared state to a container object for the set
Yeah I think that's a good way to describe these "ECS-ish" patterns.
They are.
I have not had to use cyclic references ever, except once doing experiments with fully connected graphs, that was very unusual
If you're doing a lot cyclic references Rust is not the right choice. Horses for courses
But are you sure you're using the best algorithm?
Doesn't it just come down to the question of who owns the node?
If it's a tree, and parents are never removed before children, just make the child owned by the parent and keep a weak reference to the parent.
If it's a general graph, and vertices can exist or not exist regardless of edges, keep a list of them independent of the edges, and keep weak references in the edges.
If it's a graph where a one or a few roots exist, and nodes exist as long as there's a path from a root node to them, that sounds like a classic use case for Rc<>.
Is there a common use case I'm missing?
https://jacko.io/object_soup.html
Agreed that I wish more beginner Rust books had a section about this. The pattern is quite simple, but it's hard for beginners who get stuck to realize that they need it.
https://www.kernel.org/doc/html/v4.18/driver-api/uio-howto.h...
And yes the Python interop is excellent.
I have been programming in Python for the past 6 years, I know all sorts of obscure details, and with rust, I just don't need to think about all of those issues.
Not really surprising, given that you have C and C++ background. That's what I was trying to highlight. Rust isn't a confusing or unproductive language as many project it to be - if you have the conceptual understanding of what happens on the hardware. Especially about stack frames and RAII. If you know those, the borrow checker complaints will immediately make sense and you will know how to resolve them.
Add rust-analyzer (Rust's language server) to it, you get real-time type annotations and a way to match types correctly in the first attempt. In my experience Rust also helps structure your program correctly and saves a ton of time in debugging. All in all, Rust is a fast way to write correct programs.
What reading materials will help me level up my understanding of this?
I have reasonable understanding of "what happens on the hardware" (been writing kernel code for years), know modern C++ (with RAII and stuff) and Rust is confusing and unproductive language for me.
Some people pick up rust quickly because it clicks into their brain early, some take longer or end up bouncing off.
Back then, people were debating the design decisions that led to the borrow checker, in public for everyone to see (on Reddit and IRC). They were trying to avoid memory safety issues in Firefox and Servo. They were even a bit surprised to discover that the borrow checker solved many concurrency bugs as well.
Then I got into Haskell, and functional programming, that made thinking about traits, immutability, and all functional aspects a breeze.
Then finally, I got into rust again, use it at work and personal projects. Somehow I managed to rewrite a project that took me 4 months in Python in about 4 days. It was faster, more robust, cleaner, and I could sleep at night.
This is factually impossible.
For anything larger than (very) small programs, Rust requires an upfront design stage, due to ownership, that it's not required when developing in GC'ed languages.
This is not even considering more local complexities, like data structures with cyclical references.
How do you outright deny something as subjective as my personal experience? Besides, I'm not the only one in this discussion that made the same opinion.
> For anything larger than (very) small programs, Rust requires an upfront design stage, due to ownership, that it's not required when developing in GC'ed languages.
While GC'ed languages allow you to skip a proper initial design stage, it's a stretch to claim that it's not required at all. In my experience using Python, while the initial stages are smooth, such design oversights come back and bite at a later stage - leading to a lot of debugging and refactoring. This is one aspect where Rust saves you time.
> This is not even considering more local complexities, like data structures with cyclical references.
I'm not going to dwell on cyclical references, since there's another thread that addresses it. They point out a way to make it as easy in Rust as it is in GC'ed languages.
Meanwhile, the upfront architecture and data structure design isn't as complicated as you project it. Rust is mostly transparent about those - even compared Python. How well do you understand how Python manages Lists, dictionaries or even objects in general? I often find myself thinking about it a lot when programming in Python. While you need to think upfront about these in Rust, there's actually less cognitive overhead as to what is happening behind the scenes.
They didn't specify.
The only time I find I have to "fight the compiler" is when I write concurrent code, and you can sidestep a lot of issues by starting with immutable data and message passing through channels as a primitive. It's a style you have to get used to, but once you build up a mental library of patterns you can reasonably be as fast in Rust as you are in Python.
Every language requires this (if you want robust code), most just let you skip it upfront ... but you pay dearly for doing so later.
Having an editor with rust-analyzer running is massively helpful too since ownership issues get highlighted very quickly. I can’t imagine dealing with any language larger than C without a smart editor. It can be done but why?
I still find async annoying though.
My biggest source of friction with Rust (other than async) is figuring out how to write code that is both very high performance and modular. I end up using a lot of generics and it gets verbose.
> This is factually impossible.
> For anything larger than (very) small programs, Rust requires an upfront design stage, due to ownership, that it's not required when developing in GC'ed languages.
It seems that is not factually impossible.
Now, answering your question: It could be useful to use boxed types and later optimize it, so you get the benefits of rust (memory safety, zero cost abstractions) later, without getting the problems upfront when prototyping.
No it isn’t Both languages comprise much more than their memory management handling. Even if your premise is true, the conclusion does not follow.
Factually it's not. It may be true that in a very very idealized thought-experiment, when someone has a perfect knowledge, never makes mistakes, doesn't have preferences, can type arbitrary fast etc, python needs fewer keystrokes, fewer keywords or such, thus is faster. Who knows. But in reality none of the assumptions above hold, also literally everything plays a much bigger role in development speed anyway.
It's nearly the opposite. For larger programs in Python, you need an upfront design stage because the lack of static typing will allow you to organically accrete classes whose job overlap but interfaces differ.
Meanwhile, Rust will smack you over the head until your interfaces (traits) are well-organized, before the program grows enough for this to become a problem (or until you give up and start over).
How do I know? I'm stuck with some larger Python programs that became a mess of similar-but-not-interchangeable classes. RiiR, if I ever have the time.
You can also install pre-commit and mypy, and have static typing.
Definitely not - mypy's pretty good these days, and lots of people use it.
> But Rust's type system goes beyond just data types - lifetimes are also a part of the type system. I don't know how you can tack that on to Python.
Well, Python's objects are generally garbage collected rather than explicitly destroyed, so I don't think it'd make sense to have lifetimes? They don't seem a correctness thing in the same way that types do.
File handles, iterators, mutex guards, database transaction handles, session types, scoped threads, anything where ordering or mutual exclusivity matters.
I strongly agree from my own and my peers experience with the sentiment that latency from zero to running code is just higher in Rust than Python or Go. Obviously there are smart people around and they can compensate a lot with experience.
But nothing that Rust does slows me down because I’m used to it.
Having a robust type system actually makes refactors a lot easier, so I have less up-front planning with Rust. My personal projects tend to creep up in scope over time, especially since I'm almost always doing something new in a domain I've not worked in. Whenever I've decided to change or redo a core design decision in Python or Go, it has always been a massive pain and usually "ends" with me finding edge cases in runtime crashes days or weeks later. When I've changed my mind in Rust it has, generally, ended once I get it building, and a few times I've had simple crashes from not dropping RefCell Refs.
If you are only writing small programs where perf doesn't matter, crashing doesn't matter, design doesn't matter - Python will be faster than Rust, because the only thing that matters is how you can write the code from 0 to "done". You can jump right in, the design stage is superfluous & unnecessary. There is nothing to optimize, the default is good enough.
If you are doing slightly more than that, Python and Rust become about even, and the more you need those things, the better Rust becomes.
Now I'm wondering about demographics. Are people who love Python more likely to love Rust as well?
The rust-analyzer language server can autocomplete the available methods for a value.
>I know rust gives memory safety and how important that is, but the ergonomic is really bad. Every time I write some rust I feel limited. I always have to search libraries and how to do things. I cannot just "type the code".
You don't have to search libraries and figure out how to do things in Zig?
I suspect Zig libraries feel easier because they're doing easier things. I bet if you try to draw a triangle with the Vulkan API in Zig, you'll find yourself looking at the reference docs a lot.
For me, it shares the most important benefits of Rust but with quite a lot more ergonomic coding model.
But me, I prefer to manipulate registers directly. Especially with "exotic" MCU where you have to execute write/read in specific number of CPU cycles. Rust makes that very hard.
Rust forces you to figure out ahead of time where each bit or byte is going to go and on which thread and using which mutation scheme. I’m happy to play the game, but it feels tedious for anything short of a parser or a microcontroller.
It messes with my process because I like to get something working before I determine the best API structure for it
I can get 90% of the performance with Swift and it flows much more easily, even though Rust’s type system is more powerful.
You can just clone and be on your merry way; you don’t need to worry about perf-related things if you don’t want to.
Swift makes every type implicitly copyable on the stack, including object pointers (through ARC), so you don’t have to clone. You can even pass functions as variables without any hoops.
I love lots of things about Rust, though, and will continue to use it for lots of things. Cross-platform things, for one!
Would add Go to the list, but only when I really have to.
Nim and Crystal could be alternatives, but don't seem to have big enough communities, at least for what I do.
However I do agree with the conclusion, Rust is a great language for scenarios where no form of automatic memory management is allowed, kernels, specific kinds of drivers, GPGPU programming, as general purpose, there are more productive alternatives, equally safe.
1) Modern Java is almost as good as C# with some things I can't give up in Java (static imports => succint code, Groovy Spock => succint tests)
2) Kotlin is better than C#
3) JVM has much much bigger ecosystem (almost all the apache projects are JVM oriented) and default web framework is much less code to type (SpringBoot) is much more productiv
4) JVM has wider variety of langs
For those reasons IMHO if you are small-mid company (or startup) it's wiser to choose JVM.
Also Java lost the attention span of the gaming industry, besides Android casual games and Minecraft, there are hardly anyone else paying attention to it.
Want a fastest possible library? It's in C++, and not portable to Win/Mac. So good luck with wrap + porting it.
Want a decent implementation of an algo? It usually exists for Java but not for C#. Hope you like writing it from scratch.
Want a C# implementation of an algo that doesn't allocate to the Nth degree. Again, write it yourself.
But ok, maybe Unity has a good ecosystem... And they fucked it over a barrel.
I also like Java, but c'mon no decent algorithms being implemented in C#? That is already approaching zealotry.
In C# the solution is half baked or archived or abuses allocation.
I think Unity has way more with C# adoption in game dev than innate C# qualities.
Sure you could use YAML parser, but it allocates everyone and their mother. Can you find a Fluent localization in C#? Sure, but its outdated and archived. Ok, but basic RoaringBitmap implementation? The repo is archived and not fully complete.
Why C# is used in game dev is incidental. It has more to do with Unity and XNA/FNA than any concrete quality of language modulo value types (but then again, most C# libraries don't focus on avoiding allocation and are just as happy as Java to construct a complicated hierarchy of classes).
Static import are also available in C# for quite some time now (c# 6, released in 2015, and in C# 10 you can even make this import global for for project).
I haven't used Kotlin, is there any killer feature compared to C#? (except more succinct code in certain cases?)
Also since it's Jetbrains - IDE integration is superior compared to anything C# can have (including Rider...)
Java doesn’t support C interop. For many desktop and embedded projects this is a showstopper, here’s an example https://github.com/Const-me/Vrmac/tree/master/VrmacVideo That C# code directly consumes V4L2 and ASIO Linux kernel APIs, and calls unmanaged user-mode DLLs like libfdk-aac.so and liba52-0.7.4.so.
Native stack and value types in C# reduce load on GC, and the number of complicated tricks required from JIT compiler. This in turn helps with startup performance. This is critical for command-line apps, and very desirable for desktop apps.
Another thing missing in Java is intrinsics support, both scalar like popcnt, bitscan, BMI, etc., and SIMD like SSE and AVX.
> Java doesn’t support C interop. For many desktop and embedded projects this is a showstopper, here’s an example https://github.com/Const-me/Vrmac/tree/master/VrmacVideo That C# code directly consumes V4L2 and ASIO Linux kernel APIs, and calls unmanaged user-mode DLLs like libfdk-aac.so and liba52-0.7.4.so.
Part of Panama: check out the "Foreign Function & Memory API" [0]. The official docs [1] say it is a preview in 21 but it got stabilized in Java 22 (isn't out yet).
> Another thing missing in Java is intrinsics support, both scalar like popcnt, bitscan, BMI, etc., and SIMD like SSE and AVX.
Also part of Panama: see the "Vector API" JEP [2].
> Native stack and value types in C# reduce load on GC, and the number of complicated tricks required from JIT compiler. This in turn helps with startup performance. This is critical for command-line apps, and very desirable for desktop apps.
This is part of Project Valhalla [3], they're adding value types and actual generics, among other things.
That said, most of these are not done / not in a stable LTS Java release yet. We'll see how much better it'll be compared to C# (if at all) once they land.
[0] https://openjdk.org/jeps/454
[1] https://docs.oracle.com/en/java/javase/21/core/foreign-funct...
Most real-live C APIs are using function pointers and/or complicated data structures. Here’s couple real-life examples defined by Linux kernel developers who made V4L2 API: [0], [1] The first of them contains a union in C version, i.e. different structures are at the same memory addresses. Note C# delivers the level of usability similar to C or C++: we simply define structures, and access these fields. Not sure this is gonna be easy in Java even after all these proposals arrive.
For a managed runtime, unmanaged interop is a huge feature which affects all levels of the stack: type system in the language for value types, GC to be able to temporarily pin objects passed to native code (making copies is prohibitively slow for use cases like video processing), code generator to convert managed delegates to C function pointers and vice versa, error handling to automatically convert between exceptions and integer status codes at the API boundary, and more. Gonna be very hard to add into the existing language like Java.
> "Vector API" JEP
That API is not good. They don’t expose hardware instructions, instead they have invented some platform-agnostic API and implemented graceful degradation.
This means the applicability is likely to be limited to pure vertical operations processing FP32 or FP64 numbers. The rest of the SIMD instructions are too different between architectures. A simple example in C++ is [2], see [3] for the context. That example is trivial to port to modern C#, but impossible to port to Java even after the proposed changes. The key part of the implementation is psadbw instruction, which is very specific to SSE2/AVX2 and these vector APIs don’t have an equivalent. Apart from reduction, other problematic operations are shuffles, saturating integer math, and some memory access patterns (gathers in AVX2, transposed loads/stores on NEON).
> most of these are not done / not in a stable LTS Java release yet
BTW, SIMD intrinsics arrived to C# in 2019 (.NET Core 3.0 released in 2019), and unmanaged interop support is available since the very first 1.0 version.
[0] https://github.com/Const-me/Vrmac/blob/master/VrmacVideo/Lin...
[1] https://github.com/Const-me/Vrmac/blob/master/VrmacVideo/Lin...
[2] https://gist.github.com/Const-me/3ade77faad47f0fbb0538965ae7...
My point is if you are doing business (especially web) apps. Use one of JVM langs insted of C# because ecosystem is much bigger (and it has fresher langs as well like Kotlin - if that's what you care about)
Despite having to spend extra time translating C API headers into C#, the productivity gains of the higher-level memory safe language were enormous.
Another example, I have shipped commercial embedded software running on ARM Linux, and based on .NET Core runtime. The major parts of the implementation were written in idiomatic memory-safe C#.
> doing business (especially web) apps
Well, these business web apps are precisely the long-running servers I have mentioned. Still, the software ecosystem is not limited to that class of problems, and due to different tradeoffs Java is not great for anything else.
#2 => don't know enough about Kotlin to comment
#3 => agree but quality > quantity
#4 => not terribly important to me (Clojure is cool but it's not "switch to the JVM" level cool)
My biggest issue with C# though is how badly exceptions are handled given that it is a statically typed langauge. I wish functions explicitly defined the exceptions it can throw since a minor package bump could add an exception without your compiler warning you that it isn't handled. I much prefer Rust, Go and Zig's error handling to C#'s since those kind of issues don't happen.
As clarification for the audience, it isn't the only GC enabled language with C and C++ like capabilities, in fact there are several examples since the early 1980's.
The adoption push for Java and scripting languages distorced the understanding of what was already available out there.
And those that have Java expertise but not C# seem to demur to those that do; image!
But it's still niche(around here and in the startup world) and gets lumped in with Java and together they are not "hip" or "agile" or whatever.
The only issue I have is with the .NET ... that is, building self-contained binaries to distribute.
For comparison:
* Hello World win-x64 binary self-contained in .NET 7 is around 70 MB
* The same for Go results in 1.2 MB
Also remember that standard .NET runtime does a little bit more than Go's runtime, so it might happen that even with trimming, for basic applications Go ends up having an upper hand on file size.
On the other hand, I have had Go static binaries grow up to 200 MB and being require to use UPX to make it manageable, e.g. trivy.
Also note that triming only works properly if the libraries have taken the effort to be trimmable, as the linker errs on the safe side and won't trim unless certain that it is really dead code, and not called via reflection.
dotnet publish -c release -p:PublishAot=true- There is no point in chasing smallest possible binary size if it trades off performance and features one wants to use in production scenario, or comes with other tradeoffs that sacrifice developer productivity. I don't see anyone complaining about the size of GraalVM native images. As long as binaries are reasonably sized, it's not an issue.
- dotnet publish -c release -p:PublishAot=true definitely produces smaller binaries than Go as of .NET 8 (and no, you cannot use the argument that it's not released yet - it's in RC.2 which is intended for evaluation for adopting .NET 8 scheduled for release next month)
I have built a Win32 desktop app with its core logic in Go; and then re-built from scratch using .NET (v7). The core logic involved a fairly complicated keyboard input processing based on bunch of config files.
- Final binary of .NET ~ 14 MB
- Final binary of Go ~ 2 MB
And I was upset that they were so big. Sometimes I used UPX. Or I kicked out all Delphi GUI libraries, and created the GUI with the Win32 API calls directly. I got 50 KB Hello Worlds.
15k May 3 2019 quickrun.exe*
Win32 GUI Application that spawns Window and ask for alias to run. Pure Win32 API, written in C (Mingw).
I literaly looled at 11MB hello world of .net or 1.2MB Go..
I do not have Windows 98 anymore. But I still have Delphi 4 installed under Wine, so I just tried it out.
Just showing a messagebox from windows gives 16k
Using the sysutils unit though, puts it at over 40k. And with classes, it becomes 57k. Not sure what they pull in. sysutils contains number/datetime parsing and formatting, and exception handling. classes has basic containers and object oriented file handling.
But what libraries are you lacking?
And type erasure isn't as negative as you seem to make it.
Bunch of really obscure use cases - fluent localization, roaring bitmaps and so on.
I've been out of the windows development game for a long time, so I haven't used C# since it strictly required a VM... what's pre-compiled C# development like nowadays? Are there major caveats? If you can emit plain old binaries in C# with no runtime dependencies, that would make it a truly compelling language IMO.
And as another question, what's the cross-platform (mainly Linux) support like in AOT-compiled C#? If it's just as good as in Windows and emits plain executables, I would probably consider it the best place to start for any new project. (Something tells me it's not...)
On Microsoft side the Singularity and Midori experiments used AOT.
They influenced the AOT toolchains for Windows 8 store apps with MDIL (Singularity/Bartok), and Windows 10 store apps with .NET Native (Midori/Project N).
Now there is Native AOT, which supports CLI and native libraries,.NET 8 extends that to EF and ASP.NET frameworks. For GUI applications, maybe only fully on .NET 9.
Mono AOT has had support for ages, being used on iOS, Android, and Blazor.
Finally there is IL2CPP and Burst compiler from Unity.
IMO a good GPR is memory safe (no C, C++, Zig), is easy to use(no Rust), has strong static typing (no Perl, Python, Ruby) and is "stable" (no Scala). Lots of choices remain: Java, Kotlin, Ada, D, OCaml..
Not sure if much has changed (it was a while back), but my biggest problem was with finding and using 3rd party libraries (mostly for the boring stuff like DB connectivity, JSON/YAML parsing, logging, etc.).
E.g. even now if I search for "zig mysql library", the tops hits are all about people discussing it on reddit, than any actual library.
You can always improve it, but there's no need to. With Rust, I was never happy, even after 5 years, I was still thinking about better abstractions and implementing more traits, making it more generic, etc.
Zig doesn't have any of that. It's very interesting combination of low-level, predictable code, with meta-programming, where you get some of that abstraction back.
i.e. Zig does not have generics, but your function can return type, so generic list is just a function which returns a newly created struct.
If a function is called in comptime, it can also return types. So for example:
// this is a function which accepts a type
// if you accept type, you also have to restrict the arg to be comptime
// if the arg is comptime it still does not mean that the function cannot be called in runtime,
// but in this case, it returns type, so it is comptime-only function
// there are also cases where this is not true, like std.mem.eql(u8, a, b) which accepts type,
// but can be called in runtime because it does not return type
fn Wrapper(comptime T: type) type {
return struct { value: T };
}
const F32Wrapper = Wrapper(f32);
// @TypeOf(x.value) == f32
var x: F32Wrapper = .{ value: 1.0 }And you just keep refactoring/improving/desperately trying different ideas, because it never feels right.
It's ok if you don't agree but pls don't try to make my responses look like I like Rust, I don't and I'd happily get those years back if it was possible.
Rust feels like totally missing the sweet spot for me. It's way to pedantic about low level stuff for writing higher level applications, but way to complicated for embedded or writing an OS. In the former case I would rather take a C++, Java, Haskell, OCaml or even Go, and maybe sprinkle some C, and in the latter case C in macroassembly mode is far more suitable.
I still have a feeling that original vision of Graydon Hoare (i.e. OCaml/SML with linear types, GC, stack allocations, green threads and CPS) would be a much better language.
We as a profession have proven that we can’t write unsafe code at scale and avoid these problems. You might be able to in hand whittled code you write but what happens when other people work on it, it gets refactored, someone pulls in a merge without looking too closely, etc., or even maybe you come back two years later to fix something and have forgotten the details.
Having the compiler detect almost all memory errors is necessary. Either that or the language has to avoid this entirely. Rust is the former class unless you use unsafe, and the fact that it’s called “unsafe” makes it trivial to search for. You can automatically flag commits with unsafe in them for extra review or even prohibit it.
Unless the "poor ergonomics" and lack of shortcuts are explicitly what provides the static memory safety.
Apart from the static memory safety USP, Rust is repeating too many problems of C++ for my taste, and at a much faster pace.
The problem is that there is too much syntax, too many symbols, and too many keywords. I just keep forgetting how to use impl and lifetimes and single quotes and whatnot. It makes it really tough to use as an occasional language. And if I can't do that, then how can I get confident enough to use it in my job?
https://aardappel.github.io/lobster/memory_management.html
It uses compile-time reference counting / lifetime analysis / borrow checking. Which is mostly inlined to the point that there is none of the sort in the compiled output and objects can even live on the stack. It basically looks like Python but is nothing like it underneath, and of course with no GIL. You can run it on JIT or compile it to C++.
There's also Koka with the Perceus algorithm on compile time and looks like a much cleaner language than Rust. It also tracks side effects of every function in a type where pure and effectful computations are distinguished.
This is exactly how I feel about Rust.
There are some good ideas in there, hiding behind a horrible language design. I am waiting for someone to provide a more developer friendly alternative with the simplicity of C or Go.
FWIW there is interest in adding bounds checking to C [1]. That discussion includes feedback from at least one member of the C standards committee.
[1] https://discourse.llvm.org/t/rfc-enforcing-bounds-safety-in-...
https://devblogs.microsoft.com/cppblog/build-reliable-and-se...
Rust will reject a lot of sound programs, and that's a huge performance hit. You are hitting the incompleteness wall with multiple mutable borrowing, closures in structures are a huge source of pain as well. And usually the answer from the community is "use handlers instead of pointers", but this gives you, surprise surprise, manual resource management alike that in C, and the compiler won't help much.
Of course this is all subjective but for me the ergonomics of Rust is far too bad. It's a good step in right direction along with ATS, but I really hope we could do better than this.
Having used Rust for a long time it’s definitely the biggest source of confusion and headaches.
> I always have to search libraries and how to do things.
Once you pass the initial curve with crutch like Copilot, then you can be almost as productive (if not more, considering refactoring and testing) with your native first coding language
I know rust gives memory safety and how important that is, but the ergonomic is really bad. Every time I write some rust I feel limited. I always have to search libraries and how to do things. I cannot just "type the code".
Also the type system can get out of control, it can be very hard to actually know what method you can call on a struct.
I still think rust is a great tool, and that it solves tons of problem. But I do not think it is a good general purpose language.