📜 ⬆️ ⬇️

A fresh look at Rust

I have been programming on Rust for quite some time, but in fact it does not really matter. Rust is so dynamic that it’s worth to be distracted for a couple of months, and you’ll have to write in a different language. However, one thing remains the same: the vector of development. With every update, every modification, the language gets better and better.

The changes still have no end in sight, but even now the language already seems more stable than a few months ago, and some stable API design patterns are starting to appear. I thought it was time to explore all this deeper and decided to rewrite my library for redis .

Where is the Rust niche?


Most of all I work with three programming languages: Python, C and C ++. With the latter, I have a very contradictory relationship, because I am never sure which part of the language I should use. C is straightforward because simple. With ++ it also contains a bunch of features, among which you choose a lot of used by you, and at the same time, almost guaranteed someone else will choose something else. And worst of all, when holivary begin. I equally will not experience love either for STL or boost, and it started even before I got involved with game development. And every time this topic pops up, there is at least one person who says that I am wrong and do not understand the essence of the language.

Rust for me just fits into the area of ​​using Python, C and C ++, but it occupies this niche in different categories. I use Python to write both small utilities and scalable server applications. And it suits me because it has a large ecosystem, and when something breaks, it can be quickly debugged.
')
Python, unlike other dynamic languages, has one interesting feature: it is very predictable. Maybe this is due to the fact that I am less dependent on the garbage collector than in other languages: for me Python is equal to CPython, and CPython means reference counting. I am the person who breaks the cycles by introducing weak links, and who adds a check on the number of links before and after the request to make sure that no cycles are formed. Why? Because I like that you can explain how the system works. I'm not crazy enough to turn off the cycle collector, but I want everything to be predictable.

Yes, Python is slow, it has big problems with parallel code, the interpreter is weak and sometimes it seems that it should work differently, but in fact it doesn’t cause me much trouble. I can run something, go away for a month, come back - and it will still work.

Rust is turned on working with memory and data, which is very similar to C and C ++. But unlike these two languages, it is much more like Python in terms of API programming due to type inference and a well-written standard library that satisfies all the needs of the programmer.

Beat against the wall


It's funny that at the beginning of programming on Rust is like a constant beating against the wall. Yes, this is not Python, and a lot of things that are familiar to him do not work in Rust. At the same time, this is not C ++, so dependency checking will be your biggest enemy. You will often wonder: after all, this should work, so what the hell does this stupid thing think that it knows something better than me and forbids me to do it?

The truth is that the borrow checker is not perfect. It protects against dangers, but sometimes it prohibits too much. Based on my experience, I can say that he is in fact wrong much less often than you think; just need to start thinking a little differently. And most importantly, it sometimes prevents the most dangerous design errors, which are then the hardest to fix. Python uses GIL to work with threads, and it is really necessary there since the moment the language itself appeared. The interpreter is written in such a way that now it is almost impossible to fix anything.

If you try, you can make a mistake with the decision making in parallelization and in Rust, but for this you really have to try. Language makes you think more, and I think it's good. I don’t want to stoop to preaching the thesis “OOP is a billion-dollar error”, but I think that the code that people write mostly depends on the programming language. We write object because it is simple in C ++, Java, Python. Yeah, and the birds become objects of the class "Animals". If you select such a tool, then you start thinking a little differently, and that is good. CPU power is not growing so fast, and it is already meaningless to consider only one object per unit of time. You have to talk more about collections and the necessary transformations.

Rust inspires


Me programming on Rust brings joy. Yes, I still can not agree with everything that the language makes me do, but I can say that I have not received so much pleasure from programming for a long time. The language gives me a bunch of new ideas for solving problems, and I just can’t wait for a stable release.

Rust is inspiring for many reasons. And the main one is that it is practical. I have experience with Haskell, I tried Erlang, and none of them looks like a practical language. I know that many programmers adore them, but these languages ​​are clearly not for me.

Rust is one of those things that anyone can try, and it will be fun. First of all (until you come across a compiler bug), it will not crash. And it will produce cute messages with compilation errors. And he also has a package manager with dependency tracking, so you can start using other people's libraries without fear of stumbling upon a rotten or already non-existent ecosystem. Working with packages in Python has evolved quite strongly over the past few years, but this is still one of the most disappointing parts of it. Cargo , Rust's package manager, is only half a year old, but it has a permanent maintainer, and it's great to use.

Even a top quality installer. It provides a compiler, a documentation utility and a package manager. And a great contribution to the pleasure of programming brings just a tool for working with documentation, which creates a great looking help out of the box. Although I want it to be a bit more like Sphinx than javadoc, this is a really good advance for the future.

But the most interesting thing about Rust is the little things. When I first started playing with it, I was amazed at the good support of FFI: besides the fact that you can simply call something from the sish libraries, the compiler finds them and links them. And such things in the language hidden indescribable set. There is an include_str! macro include_str! which will read the file at compile time into a string in a binary, wow! And you can even tighten the environment variable in the executable file, for example.

API Design


The most interesting thing now in working with Rust is finding a way to properly and beautifully write an API. Rust as a language is undoubtedly more complicated than many others precisely from this point of view: as a programmer, you will be torn between writing a straightforward (as in system programming languages) and providing a beautiful high-level interface (as in Python).

And I tend to write beautiful APIs, because the language itself encourages this approach. On the one hand, the language is very expressive, on the other hand, it provides an incredible amount of possibilities.

One of these “buns” is a damn cool type system. The language is statically typed, but the type inference mechanism allows you to write really beautiful code. For example, my driver for Redis allows you to write like this:

 extern create redis; fn main() { let client = redis::Client::open("redis://127.0.0.1/").unwrap(); let con = client.get_connection().unwrap(); let (k1, k2) : (i32, i32) = redis::pipe() .cmd("SET").arg("key_1").arg(42i).ignore() .cmd("SET").arg("key_2").arg(43i).ignore() .cmd("GET").arg("key_1") .cmd("GET").arg("key_2").query(&con).unwrap(); println!("result = {}", k1 + k2); } 

And for comparison, the Python code:

 import redis def main(): client = redis.Redis('127.0.0.1', 6379) pipe = client.pipeline() rv = pipe \ .set("key_1", 42) \ .set("key_2", 43) \ .get("key_1") \ .get("key_2").execute() k1 = int(rv[2]) k2 = int(rv[3]) print 'result = {}'.format(k1 + k2) if __name__ == '__main__': main() 

Interestingly, although the library on Rust is similar in size and “clean” to Python, it is much lower level. The Python library gives each call a separate method, the version for Rust (because it is young) is just a wrapper for the low-level API, and the request must be created manually as a sequence of calls for each argument. Nevertheless, the end result for the user looks just as good. Still, Rust requires a bit more error handlers (although I avoided it using unwrap , which causes the application to terminate, but the same thing happens in the Python version, where I also skipped error checking).

A big plus in favor of the version for Rust - type safety. And this is despite the fact that in total there are only two places where types are mentioned, and these are the same places where, even in Python, coercion to the whole was used.

However, this is not the best thing we can do with Rust: it has compiler extensions that open up a whole lot of possibilities. For example, there is a library that checks Postgres SQL commands for correctness, rust-postgres-macros :

 test.rs:8:26: 8:63 error: Invalid syntax at position 10: syntax error at or near "FORM" test.rs:8 let bad_query = sql!("SELECT * FORM users WEHRE name = $1"); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to previous error 

And this is really exciting.

PS You can chat on Rust API design in Mozilla's IRC network on # rust-apidesign channel

Future


Is the memory management concept in Rust so powerful to accept it as a programming model? I'm not sure. But I believe that the language can already stand on its feet. And even if people decide that the borrow checker is not needed, it seems to me that this will not prevent widespread use of the language. Rust promises to be great, and also works great without a garbage collector.

This is an exceptional open source project. And he needs more help. Windows support is getting better and better, but it still requires a lot of work.

Source: https://habr.com/ru/post/239067/


All Articles