In this article I wanted to tell why I consider this trend as a whole harmful, and why Rust is a good general-purpose language in which you can do any projects, starting with any microservices, and ending with the scripting of the daily routine. The more complex the language, the richer the phrases composed with its help, and the better it can express the desired subject area.
Since concepts are usually studied once, but are applied many times, much more profitable from the point of view of investing their own time to study all kinds of scary words like “monodic transformers” (and, preferably, their meaning), in order to save their mental forces and spend them nice And therefore, it is very sad to see the trend of some companies to make especially “simplified” languages. As a rule, it contains Hello World, a compiler installation instruction, and the basic information on the language with a gradual complication.
In the case of Rust, this is a hand piece, and the first example is reading a number from the console and displaying it on the screen. There is still a separate creation of the variable and reading into it from the stream, but here the ideology of the Rasta imposes an imprint that it transfers the allocation of the buffer to the user.
Most likely due to the fact that the explanation of error handling occurs much later, and ignoring them as in the case of C # does not allow the Rasta paradigm, which controls all possible paths where something can go wrong. Newcomers run in panic, the guys from going say “we warned you about unnecessary complexity,” Haskell-ists say “so much difficulty for a language that even has no effects,” and the twist their fingers at the temple “did it cost a fence for only GC refuse”.
From the point of view of a person with programming experience in a language with GC, memory is not a separate type of resource. As a result, there is a whole heap of consequences: one should not forget about the correct implementation of finalizes, and the whole keyword was introduced for this (as well as Java’s try-with-resources), and the compiler should be cracked to generate for each with this in mind … Unification of all types of resources that are released automatically, and as quickly as possible after the last use it is very nice.
I’ll immediately answer the potential objection that DI containers make life a little easier, but they don’t solve all the problems. From the point of view of a person with programming experience in a manual language, in 99% of cases, it is not necessary to use smart pointers; it is enough to use ordinary links.
There is a good picture, it generally describes many languages, but drawn specifically for the case of RUST : This was especially true until the appearance of Rust 2018 when the compiler in some cases did not miss the obvious code.
After that, the person usually starts to beat the keyboard, and write articles that “even in a growing room, a coherent list will not work out normally.” Of course, you can implement it, but it is a bit more difficult than in other languages, you have to add reference counting (RC/Arc/Cell/Recall primitives) to run the number of these links because the compiler is powerless in this situation. The reasons for this are: this data structure does not fit well with the concept of ownership of Rasta, the whole language and the ecosystem as a whole are built around.
But this is true for all programming languages: attempts to implement memory management in languages with GC lead to terrible monsters working through WeakReferences with giant byte arrays, resurrecting objects in instructors to bring them back to the pool, and another terrible necromancy. Trying to get away from the dynamic nature of JS to write productive code leads to even stranger things.
Thus, in any programming language there is a “pain point”, and in the case of RUST, these are data structures with many owners. But, if we look from the application point of view of high-level programmers, our programs are arranged just in this way.
For example, in my environment, a typical application looks like a certain layer of controllers that are linked between services. And if we consider that in practice the main data structures are lists, arrays, and hash maps, it turns out that everything is not so bad.
This is very helpful in cross-platform development when you have everything going locally, and on some matrix on the CI server somewhere, something falls due to conditional compilation. I replaced everything I wanted, and then for half an hour, I tried to build a project and inserted suggestions from the compiler here and there.
In the case of C#, I have much less confidence, all the time I’m thinking, “will’t null here”, “will there be a KeyNotFoundException here”, “have I synchronized access to these variables from many streams correctly”, etc. And they are much easier to catch, and sometimes you can bring to the level of types (a good article on the topic).
RUST is an excellent language for writing absolutely any application, and not just heavily loaded exchanges, blockchains, and trading bots. Yes, yes, it is possible that times off will shower me with stones, but instead of trying to explain to the compiler in a couple of places of my bot, I could easily share the variable, I cloned it, and passed a copy.
Implementing an application on Rasmus according to all canons, eliminating all unnecessary copying is a very difficult task. But writing an application at the same time as in your favorite language, and getting a free performance increase is very real.
If you don’t manage to pass, check your data structures and their interconnections, because I gradually began to understand that borrow checker is not just a mechanism responsible for the possibility of freeing memory, but also an excellent detector of the application architecture, from why this object X depends on Y, I did not expect this! If you understand everything, but explaining the correct answer to borrow checker is too difficult, just copy the value.
RUST concepts are very powerful and work fine at the application level, which does not reflect on performance, but rather only on developer productivity, the speed of implementation of new features and ease of support. It is very sad to observe that such an excellent in all respects language is increasingly receiving the stigma of “strange and complex language for low-level geek tasks.” I hope I have shaken this harmful myth a bit, and there will be more productive and happy developers in the world.
I recently decided to put serious effort into learning the Rust programming language. What follows are my reflections on what I think those strengths are, how I’m approaching learning the language, and why I’m very excited about Rust.
As for Rust ’s relevance to Influx, I have a dream of creating a Rust -based implementation of Flux (our new scripting & query language) that uses the C++ Apache Arrow libraries, that would be embeddable in other systems (like Spark, Kafka Streams or other places). So those are what motivated me to finally take the leap, but I soon found out that those wouldn’t be the only things that make Rust a compelling language to work with.
I’ve written an interpreter before, I’m very familiar with Go, and the nice thing about this project is that it limits the scope of what I’d need to learn. Thorsten’s book comes with all the code and extensive tests so it’s easy to make sure things are correct.
I started out by reading The Rust Programming Language, which is part of the free online documentation. Reading through this particular book made me appreciate early one of the Rust community’s strengths: documentation is built into everything.
Docs for the standard library are online, or you can bring them up locally with a single command (something that’s great when you’re learning on a plane). The other amazing thing about the documentation in comments is that the examples you put into your docs will actually get built during testing, so code in documentation never falls out of sync with the actual library definitions (example from my project).
These little touches all combine to build what I think is a very solid foundation for future Rust library authors, and more importantly, users. The order you implement everything is this: first the lexer, then the parser, then the interpreter, and then go back to add features to the language in all three areas.
Converting the lexer over to Rust was a fairly straightforward process and didn’t present too many challenges beyond initial struggles with the borrow checker and compiler. Since a big part of learning a new thing is rote memorization, the mechanical aspects of building a lexer and parser are actually a good repetitive task to start hammering the syntax and vocabulary into your brain.
It’s also fun and satisfying to create a test, see it fail, then write a bit of code to make it pass. Specifically, I had to figure out how to do recursion and a nested tree structure (the AST) without having the Rust borrow checker yell at me.
I Googled around a bit and re-read some sections of the book, but I obviously needed a deeper understanding of the borrow checker. I also read other people’s accounts of “hitting a wall” in the process of learning Rust, so I figured extra effort would lead me to scaling it.
Contrary to popular belief, I don’t think great programmers have some innate ability that makes learning to code easy. I think they just fight through the hurdles in search of those few moments when things “just work” and you feel a deep sense of satisfaction.
My sense is that if you’re going to learns, you need to prepare yourself for this level of effort and frustration, but if you do, I think the payoff is worth it. This one covers some in-depth stuff about how memory is organized and frequently references C++ code in comparison, but knowledge of C++ isn’t a prerequisite for getting significant value out of it.
I tried to stick pretty close to Thorsten’s Go implementation, but I did bring in some Rust specific things. As you can see, I kept a pretty faithful port with the function names and basic code structure being largely the same.
The experienced Rust developers will probably notice my inconsistent use of moves or borrows when passing a token to a method (I should refactor). I think Rust has the most elegant patterns for error handling of any language I’ve worked with.
I suppose I did take a little time to learn how to implement local scopes and closures in the language. I had to pick up RC (a reference counted pointer) and Recall (for dynamically mutating state in the Environment associated with a function).
Although this design led to memory leaks in my implementation, that’s because it creates a cycle of reference counted structs due to the Monkey language’s closures, so they don’t get freed up. I’m wondering if there’s a way to design and structure the code around this or if I need to implement a basic garbage collector.
I’ve opened up an issue to discuss a GC for Monkey Rust if anyone is up for giving me a few pointers in the right direction. Load balancers, proxies, operating systems, databases, network queues, distributed systems, machine learning libraries, the underlying implementation for higher level languages, and probably countless other things that are not coming immediately to mind.
Contrast this to Rust, which has significantly more syntax, a model for working with memory management that few if any programmers are familiar with, and a compiler that can be more strict than even the worst disciplinarian. However, there are compelling advantages that Rust can boast, which I think warrant the initial learning curve.
We’ve had a number of production bugs in Influx due to data races that were only caught under heavy load. With Rust, the compiler forces you to “do the right thing”, which is great because then you won’t have to worry about it slipping past a code review.
In Rust there are entire classes of bugs that are simply impossible to create because of the compile time guarantees. This is the payoff, and despite the strictness of the compiler, Rust ’s bet is that if you learn the way you can be as productive (or more) than with another language.
I have more miles to travel on my Rust journey before I can validate Bland and Sendoff’s proposition, but I intend to give it real effort. After that I’ll pick up a small prototype project to create a web service that uses a C++ storage library.
After all of this, I’ll be in a good place to make real efforts on some projects I envision for Rust at Influx. I think an embeddable implementation of Flux would be a fantastic thing to create in Rust.
I think there’s a very real possibility that mountains of C and C++ code gets rewritten over the next fifty years leading to more secure systems' software built around Rust ’s guarantees. And if Rust ’s radical wager turns out to be a winning bet, it’ll become my de facto first choice for servers and systems.