Hacker News new | ask | show | jobs
by bsurmanski 2694 days ago
For anyone else looking into Rust, I've been experimenting in it for the past month. Heres my thoughts:

It advertises itself as a systems level language for the new age, but to me it feels like a functional language in disguise. The borrow checker enforces an unchained functional style (You may have 1 unique mutable reference or may have many immutable references). This sounds bad if you're used to pointer slinging, but once you get used to the peculiarities I find it forces me to write better quality code.

The package management is the most refreshing feature. It's a pain to rewrite my personal libraries in each language I use, but I found that most of the things I needed were already available at higher quality than I would have done. (And using them is a single config line).

For a 'low level language' they make it pretty hard to do some 'low level' things. Example's I've found include:

* writing a doubley linked-list. Consensus is "don't use linked-lists. The stdlib has better tools

* Reading a file into a struct. They make it surprisingly difficult to say "hey, read these 10 bytes, its this struct". Consensus is to use more structured file formats like json or protobufs. There's libraries for reading those things.

Error handling is similar in style to Go, but it get rid of a lot of the boilerplate. The '?' operator effectively acts as if err return err.

The macro system is really nice. I don't write many macros, but it does mean I can use other people's powerful macros. My favorite are 'include_bytes!', 'lazy_static!' and 'dbg!'. The new procedural macros are pretty wacky, essentially allowing you to parse or rewrite the AST. A powerful example I've seen is static checks; This [0] example writes a compile time check to assert that structs do not contain a member named 'bees'.

Overall it's been a fun language to mess around with.

[0] - https://tinkering.xyz/introduction-to-proc-macros/

4 comments

> They make it surprisingly difficult to say "hey, read these 10 bytes, its this struct".

It's really easy, but it's `unsafe`:

    let my_bytes = [...];
    let my_struct = unsafe {
        std::mem::transmute(my_bytes);
    };
It's necessarily unsafe, because Rust has no way of knowing what invariants the struct is responsible for upholding. If the struct contains a Vec, for example, then transmuting it from bytes will probably give you garbage pointers and a security vulnerability.

I think something very interesting has happened with `unsafe` in the Rust community as the language has grown. There are lots of things that are "easy with `unsafe`", but everyone seems to round that up to "hard". I think that's a Very Good Thing, because it means that safe code is powerful enough and convenient enough that not using it is seen as a big deal.

slice::from_raw_parts_mut makes it trivial to reinterpret some bytes as a struct. Beware, though, that Rust doesn't have a stable ABI as C does, and can reorder fields and do other magic freely. What you're doing is already incredibly dangerous in C, but it's extra dangerous in Rust.

By the way, regarding doubly linked lists, standard practice when dealing with anything that doesn't fit nicely with Rust's ownership model is to either (a) write a nice high-level abstraction over it that uses unsafe code, or (b) give up and use array indices to circumvent the borrow checker, which has the added bonuses of having smaller "pointers" and much better locality.

> to me it feels like a functional language in disguise

I disagree on the "disguise" part.

Yet, I don't see why you consider it an opposite of being a system language.

Maybe I'd call Rust a "resource-constrained" language than low-level. The usecase being you want no GC, for memory inflation or CPU pauses, but suffer a bit in overall CPU time and memory locality.
Why do you think you'd suffer in CPU time and memory locality?
Sometimes you have to put stuff behind pointers, use runtime checks for safety, or change how you represent data to something suboptimal.
Sure, but the inverse is true too. Sometimes you can get better performance in Rust, because the borrow checker allows you to do things you would never do in C/C++. For example, passing around array slices is very common in Rust, but the cases where you'd do it in C or C++ is much more limited, because it's so error prone. You have no guarantees that the memory being pointed to won't be pulled out from under your feet.
I've had Rust hinder my ability to pass around array slices more than help.
The point is that while it's easy to do in C++, it's extremely error prone.

Rust encourages you to write code that works with things like slices and references instead of copying, because the compiler won't let you use them in a way that is error prone.