Fresh look at Rust

Original author: Armin Ronacher
  • Transfer
I have been programming in Rust for quite some time, but actually it does not really matter. Rust is so dynamic that it takes a couple of months to get distracted and you have to write in a different language. However, one thing remains unchanged: the vector of development. With each update, with each modification, the language is getting better and better.

There is still no end in sight, but even now the language seems more stable than a few months ago, and some robust API design patterns are beginning to appear. I thought it was time to explore all this in more depth 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 ++. I have a very conflicting relationship with the latter, because I'm never sure which part of the language I should use. Xi is straightforward because it is simple. C ++, however, contains a bunch of features, among which you choose a lot of the ones you use, and at the same time, it is almost guaranteed that someone else will choose something else. And worst of all, when holivars begin. I equally will not feel love for either STL or boost, and it started before I even got involved in game development. And every time this topic pops up, there is at least one person who claims to be wrong and do not understand the essence of the language.

Rust for me just fits into the field of use of 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 debugged very quickly.

Python, unlike other dynamic languages, has one interesting feature: it is very predictable. Maybe this is because I'm less dependent on the garbage collector than in other languages: Python is equal to CPython for me, and CPython means reference counting. I am the kind of person who breaks loops by introducing weak links, and who adds a check on the number of links before and after the query to make sure that loops do not form. Why? Because I like that you can explain how the system works. I'm not so crazy as 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 rather weak, and sometimes it seems that it should work differently, but in fact it does not cause me big problems. I can start something, leave for a month, return - and it will still work.

Rust is turned to work 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 a programmer.

Beat against the wall

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

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

If you try, you can make a mistake when making a decision when parallelizing and in Rust, but you really have to try to do this. 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 mistake,” but I believe that the code people write mostly depends on the programming language. We write objectively because it is simple in C ++, Java, Python. Yeah, and the birds become objects of the "Animals" class. If you select such an instrument, then you begin to think a little differently, and that’s good. CPU capacities are already growing not so fast, and it is already pointless to consider only one object per unit time. We have to talk more about collections and the necessary transformations.

Rust inspires

Programming in Rust brings me joy. Yes, I still can’t agree with everything that the language makes me do, but I can say that I haven’t 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 practical. I have experience with Haskell, I tried Erlang, and none of them look 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 everyone can try, and it will be fun. Firstly (until you come across a compiler bug), it will not crash. And it will produce nice messages for 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 filthy or already non-existent ecosystem. Working with packages in Python has evolved quite a bit over the past few years, but it's still one of its most disappointing parts. Cargo , Rust's batch manager, is only six months old, but he has a regular maintainer and is fun to use.

Even the installer of the highest quality. It provides a compiler, a documentation utility, and a package manager. And a great contribution to the pleasure of programming is brought just by the tool for working with documentation, which gives an excellent looking help out of the box. Although I want it to be a little more similar to Sphinx than to javadoc, it is a really good deposit for the future.

But the most interesting thing about Rust is the little things. When I was just starting to play with it, I was struck by the good support of FFI: besides the fact that you can just call something from the library libraries, the compiler finds them and links them. And such things are hidden in the language an indescribable multitude. There is a macroinclude_str!that will read the file at compile time into a string in a binary, wow! And you can even drag environment variables into an executable file, for example.

API Design

The most interesting part of working with Rust right now is finding a way to write the correct and beautiful 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 straightforward (as in system programming languages) and providing a beautiful high-level interface (like in Python).

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

One of these “goodies” 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://").unwrap();
    let con = client.get_connection().unwrap();
    let (k1, k2) : (i32, i32) = redis::pipe()
    println!("result = {}", k1 + k2);

And for comparison, the Python code:

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

Interestingly, although the Rust library is similar in size and "cleanliness" to the Python library, 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 has to be created manually as a sequence of calls for each argument. Nevertheless, the end result for the user looks just as good. And yet Rust requires a bit more error handlers (although I avoided using this unwrap, which causes the application to terminate, but the same thing happens in the version for Python, where I also skipped error checking).

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

However, this is not the best we can do with Rust: it has compiler extensions that open up a whole sea of ​​possibilities. For example, there is a library that checks Postgres SQL commands for correctness, rust-postgres-macros : 8:63 error: Invalid syntax at position 10: syntax error at or near "FORM"     let bad_query = sql!("SELECT * FORM users WEHRE name = $1");
error: aborting due to previous error

And this is really exciting.

PS You can talk about Rust API design on the Mozilla IRC network on the channel # rust-apidesign


Is Rust's memory management concept so powerful to accept as a programming model? I'm not sure. But I believe that the tongue can already stand on its feet. And even if people decide that borrow checker is not needed, it seems to me that this will not prevent the 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 still requires a lot of work.

Also popular now: