So, a couple decades ago I was a C/C++ developer (oh and ASM!). I loved life, enjoyed the language. Then, I learned the higher level stuff. SQL, Visual Basic, ASP, C#, PHP, Python, Ruby, etc. I REALLY enjoyed that stuff, and it has fueled my career. This morning I found out about an interesting language called Hare, which kinda looks like a certain other language...then I saw this post.
I hate you. I hate you for making me hate low level programming. I mean...I don't hate it. I don't even hate memory management...but YOU...YOU sir...
I love Rust. But it seems like there's some kind of religion about allocations.
Don't use String you fool, you want &str!
Don't use Vec you fool, use &[]!
It really puts people off. Because now they have to learn all about lifetimes and that's a fucking nightmare. My advice: Use String. Use Vec. Use Hashmaps. It's all still going to be a million time faster than whatever you were doing in Python, or Java, or Go, or Javascript, or whatever.
This isn't really a Rust problem. Even Java forces you to reckon with heap vs. stack allocation with its boxed types, and you'll get annoying warnings when you use Integer when int would do.
This is just programming culture; given two ways of doing something, with one being harder but more performant, we gravitate to the harder one at all costs.
There are only two times to use boxed types in Java: when they’re in collection types that take objects (e.g. generics), or when the value needs to be nullable. The first is a language design flaw (imho) that can’t be easily fixed due to backwards compatibility desires. The second is almost always a boundary (DB to app, app to API, etc.) issue that should be resolved quickly, imho.
In existing codebases I’ve worked with, often 90% or more boxed primitives are unnecessary, introduce unnecessary errors (assumed non-null refs vs. guaranteed non-null values) and performance penalties. They have a purpose, but there are libraries that address the first concern and Optional${Type} that are more expressive for the second.
I'd discount this part entirely. Using a boxes/non-primitive type for nullability is likely a design mistake. I can go in a full rant mode, in practical terms I tend to reject pull requests that do that.
I mean. They gave example of where it might be commonplace, which is at API boundaries.
Especially at DB boundaries, there’s decisions to make. If you put yourself in a position of your db having entirely non-nullable fields, you end up in one of two situations:
1) your whole db is key/value
2) you have magic values
Making all key/value doesn’t solve the nullability issue at the code boundary if you’re representing as an object, so you’re left with magic values. I think we all know how nice magic values are.
For giggles, also checkout the amount of generated machine code in godbolt with a simple Hello world that uses &str vs String. Its like 10x the instructions :P
While I agree about million times, [micro]benchmarks that run seconds (or milliseconds) have extremely limited use, esp. when combined with non-compiled languages, that's effectively comparing compilation, guidance, and run time.
Yeah, it appears they just run a script, I don't think the code itself is allowed to say how long the test took. That's why you have to subtract like 80ms (estimating from the hello world comparison) for most of them.
If you don't understand how lifetimes and associated aspects of the language work, you might very realistically run into situations where your code will be a million times slower than the python, java, etc. equivalent, not faster. For instance, copying a massive dynamically allocated container in every iteration of a hot loop instead of passing a reference (much less likely to do on accident than in C++, but still possible), if you don't understand value vs reference semantics, copying, moving, etc, since garbage collected languages don't force you to learn these things, and passing around references to large collections is cheap in those languages.
That sounds like the problem is the container itself contains value types rather than references, but then doesn't implement copy-on-write, which is a simple way to solve this.
so then you do that, making stuff on the heap, doing extra zero'ing and so on, and your debug build gets faster, and you call it good. then someone profiles a release build, removes the allocation and watches the optimizer wipe out the entire copy because all you were really doing is touching a single field. there's no general case here.
> much less likely to do on accident than in C++, but still possible
Tangent: I can't think of a single other language that's like this: the higher-level languages tend to pass by reference, and the lower-level languages tend to have you explicitly clone things.
Well you're not going to be much of a Rust user if you don't learn the basics of lifetimes anyway. I don't mean to get all gatekeepy, but if someone's not comfortable with &str and &[T], they're missing half the point of the language and won't be able to use much library code anyway.
> If you don't care about allocations why are you using Rust in the first place?
Because Rust excels on a lot of dimensions other than "systems programming"; Cargo is fantastic, the ecosystem of packages is great, it's one of the only languages that takes cross-platform support (including Windows) seriously, and it's got sum types.
True. However, most that want to keep interoperability with Java, in order to get benefit of huge deposit of packages. That often means having to repeat Java's mistakes to maintain compatibility.
Another worthless Rust advocacy strike force comment.
No one over here was making the point Rust being a guest language, unless you want to now start discussing about GraalVM capability of executing LLVM bitcode.
"However, most that want to keep interoperability with Java, in order to get benefit of huge deposit of packages. That often means having to repeat Java's mistakes to maintain compatibility"
Or going back to the comment Ygg2 is replying to, regarding any ML derived language?
Where exactly do I assert anything regarding running on the JVM having any relation to Rust's position on the language market?
I like to pre-allocate a &str buffer at launch, then create &str variables pointing into that buffer. I separate them with 0x00 bytes so I don’t have to store an extra size counter or invent a new data type.
If you do not want to learn about lifetimes do not use Rust. The advice is also terrible since inevitably you are going to use someone elses code, which does use slices.
> It's all still going to be a million time faster than whatever you were doing in Python, or Java, or Go, or Javascript, or whatever.
Not at all. Systems languages don't magically make all code faster, they just help you write fast code. It's not that hard to tank performance if you're careless with allocations. I've seen some really slow C++, written by people who liked to use new everywhere like it's Java.
If you want not to care about this stuff, I think that Java would fit your programming style better (or Kotlin, if you dislike Java's verbosity). The JVM is quite tolerant of doing lots of unnecessary allocations.
Given the implementation of this little thing, I'm mildly curious about how easy it is/will be for a C/C++ project/programmer to use these Rust crates.
The other way around is relatively well defined and has been the subject of a lot of attention for several decades. Is the Rust-lib-called-from-C even feasible?
I feel like Rust was designed to fully replace old languages, and is now facing the same issue as every language before it - C and C++ interop (both ways).
Zig nailed it, Rust fucked it up. Time will tell if thats big enough of an issue to warrant some bigger changes to fix it.
That is mischaracterization. Rust has been intended to be able to incrementally integrate to C/C++ codebases, of course most significantly integrating Rust/Servo components to Firefox/Gecko.
See for example this rsvg presentation[1] and gstreamer blog post[2] for examples of the interop
Rust can be called from C the same way as C++ can. You need to export unmangled extern "C" symbols. Not quite frictionless but there are helper utilities for this.
If the Rust code uses the "StupidAlloc" allocator, it should work as expected. There may be some constructor/destructor code involved with its usual complications.
But it would not replace malloc/free in C code nor will it be compatible with C++ allocators for std containers.
It has been practical for ages. You define a wrapper function as no_mangle and extern "C", then compile the lib as a staticlib. When compiled, it produces a .a file you can link into your C application
Although this crate likely enforces a higher alignment (to the page boundary with mmap) regardless of what is requested, you should request a higher alignment because malloc is guaranteed to be aligned to the largest C type, which is usually either 8 or 16 bytes.
I guess they will soon deprecate the non-interactive option because of a grave security issue: it uses predictable file names (it creates files in *std::env::temp_dir(), and uses
69 comments
[ 3.3 ms ] story [ 141 ms ] threadI hate you. I hate you for making me hate low level programming. I mean...I don't hate it. I don't even hate memory management...but YOU...YOU sir...
Don't use String you fool, you want &str!
Don't use Vec you fool, use &[]!
It really puts people off. Because now they have to learn all about lifetimes and that's a fucking nightmare. My advice: Use String. Use Vec. Use Hashmaps. It's all still going to be a million time faster than whatever you were doing in Python, or Java, or Go, or Javascript, or whatever.
This is just programming culture; given two ways of doing something, with one being harder but more performant, we gravitate to the harder one at all costs.
In existing codebases I’ve worked with, often 90% or more boxed primitives are unnecessary, introduce unnecessary errors (assumed non-null refs vs. guaranteed non-null values) and performance penalties. They have a purpose, but there are libraries that address the first concern and Optional${Type} that are more expressive for the second.
I'd discount this part entirely. Using a boxes/non-primitive type for nullability is likely a design mistake. I can go in a full rant mode, in practical terms I tend to reject pull requests that do that.
Especially at DB boundaries, there’s decisions to make. If you put yourself in a position of your db having entirely non-nullable fields, you end up in one of two situations:
1) your whole db is key/value
2) you have magic values
Making all key/value doesn’t solve the nullability issue at the code boundary if you’re representing as an object, so you’re left with magic values. I think we all know how nice magic values are.
Ideology is the enemy of practicality.
You can turn them off though, and most of the culture will support you in ignoring them if that's appropriate to your use case.
https://programming-language-benchmarks.vercel.app/rust-vs-j...
For giggles, also checkout the amount of generated machine code in godbolt with a simple Hello world that uses &str vs String. Its like 10x the instructions :P
I'd consider the entire site/process flawed.
With apologies to jwz.
Java has references only by design, so you can't do that.
Tangent: I can't think of a single other language that's like this: the higher-level languages tend to pass by reference, and the lower-level languages tend to have you explicitly clone things.
Also if you are super sloppy with allocations I would expect something like Java or Go would be faster since GC is more efficient.
Because Rust excels on a lot of dimensions other than "systems programming"; Cargo is fantastic, the ecosystem of packages is great, it's one of the only languages that takes cross-platform support (including Windows) seriously, and it's got sum types.
Or, as withoutboats put it: https://twitter.com/withoutboats/status/1658536526043570179
Including some that run on the JVM and 30 years of Java, makes Maven Central certainly beat cargo.io in package count.
So there are options.
> Including some that run on the JVM
True. However, most that want to keep interoperability with Java, in order to get benefit of huge deposit of packages. That often means having to repeat Java's mistakes to maintain compatibility.
Likewise a language without flaws, is a language with no users.
No one over here was making the point Rust being a guest language, unless you want to now start discussing about GraalVM capability of executing LLVM bitcode.
"However, most that want to keep interoperability with Java, in order to get benefit of huge deposit of packages. That often means having to repeat Java's mistakes to maintain compatibility"
Or going back to the comment Ygg2 is replying to, regarding any ML derived language?
Where exactly do I assert anything regarding running on the JVM having any relation to Rust's position on the language market?
The mention was to make a point about one of those possible alternatives, available on the JVM.
Zero content on Rust's adoption.
No-shit. One is also old as Methuselah, so having a large repository of packages at that stage would mean it died already.
Plus it depends on granularity of packages. JS has
Perfection is a moot point anyway.
Also, copies are not bad and they're often a better tradeoff than microoptimizing all your lifetimes.
If you do not want to learn about lifetimes do not use Rust. The advice is also terrible since inevitably you are going to use someone elses code, which does use slices.
Not at all. Systems languages don't magically make all code faster, they just help you write fast code. It's not that hard to tank performance if you're careless with allocations. I've seen some really slow C++, written by people who liked to use new everywhere like it's Java.
If you want not to care about this stuff, I think that Java would fit your programming style better (or Kotlin, if you dislike Java's verbosity). The JVM is quite tolerant of doing lots of unnecessary allocations.
[1]: https://en.m.wikipedia.org/wiki/Bell_character
The other way around is relatively well defined and has been the subject of a lot of attention for several decades. Is the Rust-lib-called-from-C even feasible?
Zig nailed it, Rust fucked it up. Time will tell if thats big enough of an issue to warrant some bigger changes to fix it.
See for example this rsvg presentation[1] and gstreamer blog post[2] for examples of the interop
[1] https://viruta.org/docs/fmq-porting-c-to-rust.pdf
[2] https://coaxion.net/blog/2016/05/writing-gstreamer-plugins-a...
If the Rust code uses the "StupidAlloc" allocator, it should work as expected. There may be some constructor/destructor code involved with its usual complications.
But it would not replace malloc/free in C code nor will it be compatible with C++ allocators for std containers.
There is no such thing.
Fits with the Unix philosophy where everything can be done to an object through a file handle.
Also, fun is ensured if you run multiple programs in parallel using this allocator. AFAICT, if they share temp_dir, they’ll share memory.
Performance tip: run it from a RAM disk.