7 ms·
The semantics of Rust aren't simple at all. I've used a huge number of languages, both professionally and for personal projects, and Rust is the first one I'm t
by eej2ya1K 7y ago
The semantics of Rust aren't simple at all. I've used a huge number of languages, both professionally and for personal projects, and Rust is the first one I'm too stupid to understand.
("Modern" JavaScript comes close, but that's not because of semantics, it's more because everything is incredibly overengineered...
- dunefox 7y ago> Rust is the first one I'm too stupid to understand. More complicated than Haskell, etc.?
- eej2ya1K 7y agoI'm probably too stupid for Haskell, too, but in my experience the Haskell community actually acknowledges that the language is hard! The patronizing attitude of rust defenders here on HN, on the other hand, man... it just pushes me further away.
- _bxg1 7y agoIt's not that hard, it just brings very unfamiliar ideas to the table. If you've used a bunch of different languages, most other languages can be picked up pretty quickly by combining different aspects you're familiar with from the ones you know. That's not the case for Rust. It takes a few solid weeks of feeling stupid until you really start to grasp borrowing, and that experience may feel offensive to people who are used to knowing what they're doing, but a few weeks really isn't that much time in the grand scheme of things.
- ChrisSD 7y agoHmm... I'm not so sure the ideas themselves are unfamiliar. Any one who has used C or C++ has to deal with lifetimes (or at least should) and things like iterators are a very common pattern nowadays. What Rust does differently is making ownership and unique/shared references a central feature of the language in a way that is amenable to static analysis. Obviously this is often a struggle for programmers to get used to, there's no denying that, but I don't think it's the concepts themselves that are unfamiliar. Unless of course the programmer is new to low level languages.
- _bxg1 7y agoI used C++ for 4 years before trying Rust and I still felt like 50% of the latter was completely new. There's a huge difference between understanding how to manage memory and understanding how to satisfy the borrow-checker. Certain things - sometimes legitimate things! - that you could do normally are simply not allowed, because they can't be proven to be correct.
- lgessler 7y agoI think GP is using 'semantics' to refer to the emitted code, which is "simple" in the sense that it e.g. doesn't have a runtime or a garbage collector. What isn't simple is all the features of rust that are unheard of in other languages, like the borrow checker, but that is a feature of Rust's syntax, and (I am not a Rust expert, but my understanding is that) many of them "vanish" when the Rust code is compiled.
- the_duke 7y agoRust seems intimidating at first, but under the hood the language semantics are (or used to be, see below) fairly simple. The big borrow checker hurdle comes from developers not being used to thinking in/about lifetimes. It takes some getting used to because it enforces constraints that should be there for a non-GC language, but are not enforced anywhere else. But it is a hurdle you can get over after a few weeks of feeling like an idiot. At one point it just clicks. You start to design your types and code with lifetimes in mind, and the borrow checker is not a problem anymore and feels natural - and a helpful safeguard even. The other complex part is the lack of inheritance and the trait system, which is also foreign for many. I feel like if you had contact with ML languages, and Haskell in particular, you will have a much easier time. It takes a different approach for structuring code. Sadly Rust is in fact getting mroe more complex all the time though, with things like async/await added and other complex features in the pipeline (like specialization, higher kinded types, generators, ...). All of those make sense and make the language more powerful, but at the expense of simplicity.
- onebot 7y ago> The big borrow checker hurdle comes from developers not being used to thinking in/about lifetimes. It takes some getting used to because it enforces constraints that should be there for a non-GC language, but are not enforced anywhere else. This!
- roblabla 7y ago> Sadly Rust is in fact getting mroe more complex all the time though, with things like async/await added and other complex features in the pipeline (like specialization, higher kinded types, generators, ...). Some of those have fairly simple semantics though. Generators are really just very a very big enum, with a function moving through the different states of the enum. Async/await is just a fancy name for a generator that returns a Poll. Specialization is very constrained - it only applies to trait methods qualified with "default"), but I agree the current rules around it are rather complex, and they aren't even sound so they'll probably get even more complex. HKT has no concrete plan so it's hard to know how much mental overhead it will add. But it's likely to be very non-trivial too.
- jedisct1 7y agoThe main advantage of Rust is that once the app compiles, it's likely to run quite smoothly. The syntax is strict, but this avoids some bugs that would have taken time to spot at runtime. For some use cases, this is a time saver. For everything else, you may spend more time fighting the compiler than you would have done debugging these issues afterwards. But yeah, it's a language that constantly makes you feel stupid. The compiler is barfing errors that are hard to understand, for code that looks trivial.
- pornel 7y ago> Rust is the first one I'm too stupid to understand. That's a typical experience, but it's not because Rust is that complicated, but because it's different. People approach Rust with their intuition from other languages, such as C's or Java's where you can have a web of objects referencing each other willy-nilly, and are stumped by Rust's insistence on clear, single ownership, and simpler tree/DAG-like structure of program's data. Single ownership itself is simple, you just need to unlearn multiple-shared-ownership habits.
- wahern 7y agoSingle ownership isn't a preference, it's a constraint and a limitation. A constraint (in a good way) because that's the entire point of the language--preventing you from losing track of memory ownership. It's a limitation because there are some extremely important and common relationships (e.g. putting an object in multiple containers simultaneously) that cannot be expressed in Rust's type system without using unsafe, RC boxes, etc, which create tremendous friction. Yet a low-level systems language is precisely the environment where you most want and need to express these relationships and data structures cleanly and efficiently. A data structure isn't something you import from a module. A good program is composed of layers of bespoke data structures that culminate in a giant data structure tailored for a particular task. So the fact that you can just import a hash table or tree implementation (friction-free) is irrelevant; useful, but irrelevant because it's combining such data structures that is the primary task at hand when writing a non-trivial piece of software, and combining them necessarily results in complicated ownership dependencies. And this is all especially true for applications that do more than transform some input into some output in one shot. I'm not trying to criticize Rust. They took a good idea and ran with it, and expressing more complex ownership semantics is still an open problem (perhaps with no satisfying answer, ever). But minimizing or dismissing the sore points isn't constructive.
- Twisol 7y ago> It's a limitation because there are some extremely important and common relationships (e.g. putting an object in multiple containers simultaneously) that cannot be expressed in Rust's type system without using unsafe, RC boxes, etc, which create tremendous friction. That is not a difficult relationship to capture, IMHO. The problem is that it's too easy to conflate "address" and "name" in most languages. An address is the physical location of a thing. A name is a location-independent way to look up a thing. Only one thing can live at an address, but you can use a name to look up multiple addresses depending on what question you want answered. (There is a parallel here with domain names and IP addresses.) Rust associates ownership with every address. This causes serious problems when you've been using addresses as names. The solution is to use different names. For instance, let's represent a graph as a vector of nodes. Every node has an index within that vector. Given this index, we can look up any node -- but critically, having the index does not confer ownership of the node. It's a completely orthogonal concern. So a node can quite happily possess a vector of the indices of its neighbors. You might complain that this is a poor naming scheme for a graph structure, since it complicates deletion of nodes from the vector. Great! We have these exact same problems when using pointers (observe that the index plus the vector base address is the node address), but now that naming is a first-class concept, we can consider it on its own terms. There are alternative index spaces we can apply. I've used this kind of design in a compiler hobby project. The intermediate representation is a set of tables representing different information about the program. With each pass, I can easily add new tables or transform existing ones, so long as I keep the names consistent. I can easily represent relationships between entities by using their indices. And I can easily attach more information to certain entities in compiler passes by adding a new table keyed on the same index.
- gourabmi 7y agoDoes your list of language include Erlang? I'd be surprised if a language has more esoteric semantics than Erlang.
- eej2ya1K 7y agoErlang is super cool! Not really my first choice when starting something new, but that's more because Erlang's strengths don't really match the kind of projects I'm interested in!