13 ms·
am overwhelmed by the sheer beauty and mature design of this language - it almost reads like Python or as well as any compiled language can. All those lifetime
by ithrow 6y ago
am overwhelmed by the sheer beauty and mature design of this language - it almost reads like Python or as well as any compiled language can.
All those lifetime annotations are not sheer beauty or pretty, sure they are necessary but not nice to look at if you are comparing it to a higher level language.
- the_duke 6y agoYou can write Rust code with almost no lifetine annotations. That's heavily dependent on the domain, of course. But for relatively simple function signatures they are usually inferred correctly, and you can often just clone instead of requiring references.
- nobleach 6y agoYes, you can clone all day long and just reuse variable names as if you were writing JavaScript, but what does that do to your memory allocation?
- gpm 6y agoMost languages just clone all day long, it's not that bad, rust clones (like most languages) are just to the first reference counted pointer after all.
- the_duke 6y agoWell, yes and no. Eg cloning a string leads to an extra allocation and a memcopy. If you want to get a similar performance profile to GC languages, you have to stick your types behind a `Rc<T>>/Arc<T>` or `Rc<RefCell<T>> / Arc<Mutex<T>>` if you need mutability. But modern allocators hold up pretty well to a GC, which amortizes the allocations. The extra memcopying can be less detrimental than one might think.
- throwaway894345 6y ago“Yes and no” is too generous; most languages clone only very infrequently (basically only for primitives where there is no distinction between a deep and shallow copy). For complex types (objects and maps and lists) they pass references (sometimes “fat” references, but nevertheless, not a clone).
- throwaway894345 6y agoTo be clear, JS doesn’t require cloning because it has a gc. Not sure what you’re getting at with reusing variables names...
- nobleach 6y agoWhat I mean is, if one does: const obj1 = { a: 32, b: 42 }; function foo(ref) { ref.a = 0; } foo(obj1); console.log(obj1); in JavaScript, one is just using the variable name as a "holder" of some value. One doesn't have to designate that that variable is being passed by reference. If one wanted to actually copy that object, they'd have to devise a mechanism to do so. In Rust, if someone doesn't specify, using the & symbol, that something is a reference, it'll end up moving the value. Basically all I was saying is one can not approach writing Rust with a Java/JavaScript mindset. (That a variable is just a bucket holding a value). Care needs to be taken when referencing a variable as it may need to be moved, copied/cloned or referenced. In the case of copying, another memory allocation is done. So if someone approaches Rust from the standpoint of "this word represents a value, and I'm going to use it all over the place", they can find themselves blindly allocating memory.
- throwaway894345 6y agoOh, by “reuse variable names”, you simply mean that values aren’t moved, i.e., that JS lacks move semantics or an affine type system. That’s a bit different to “reusing variable names”, which you can do in Rust as well provided the variable holds a (certain kind of?) reference.
- throwaway894345 6y agoPretty sure if you’re deserializing a structure with borrowed references you need lifetime annotations. Copying things around to pacify the compiler hardly seems elegant.
- the_duke 6y agoSure, but that's a performance vs ease of use decision. Often you don't need to care about the extra allocations and can just deserialize to owned types. The code for owned deserialization certainly ends up looking more elegant.
- throwaway894345 6y agoIt’s not just performance, but you now have to go back and update every instance of that struct to reflect the change in ownership of that member if you can even change the member in the first place (e.g., you can’t change the type of a member of the struct itself is defined in a third-party package). Moreover, some instances of your struct might be in a tight loop and others might not, but now you’re committing to poorer performance in all cases. Maybe you can box it (although IIRC I’ve run into other issues when I tried this) but in any case this isn’t the “elegance” I was promised. Which is fine, because Rust is a living, rapidly-improving language, but let’s not pretend that there is an elegant solution today.
- fasterthanlime 6y agoLibraries can be (and some actually are) designed with that in mind, with COW (copy on write( types that can be either borrowed or owned. Speaking of performance, kstring (of liquid) goes one step further with inline variants for short strings. I want to say that seems like a valid concern but in practice I've seen it come up only rarely.
- rapsey 6y agoAll those? Very small amounts of an average Rust code base will have lifetimes.
- nitred 6y agoI agree it's not pretty. It took me several passes to just understand what was going on there. But ownership is something I've never come across before and it doesn't exist in Python (not to an average user anyway), so it seems a little unfair to compare Rust and Python based on that feature. However when you take a high level feature like overriding operators which can be done elegantly in Python, for a complied language, Rust's way is quite concise, readable and to my eyes quite pretty. Edit: Typos
- volta87 6y agoI think that being able to write down the lifetimes in the language is beautiful. You don't have to do it, but if you want to do it, being able to do so in a way that's verified and enforced by the toolchain beats doing so in a documentation comment. Now every time I read a doc string saying that I need to "deepcopy" something in Python for some API usage pattern to work properly I cringe.
- fasterthanlime 6y agoThat's one of the things that get me about Rust discourse: it seems that "Rust is pain in exchange for performance" is a common misconception. Rust is discipline in exchange for performance and correctness. A GC lets you be relatively worry-free as far as memory leaks go (and even then..) but it doesn't prevent a lot of the correctness problems the borrow checker would. With a checker you're forced to think: do I really want to pass a copy/clone of this? Or do I want to let that function borrow it? Or borrow it mutably?
- harikb 6y agoWhile I get the point about data-races, a “GC” doesn’t make/help you leak memory - you have simply postponed the free to a later point in time. Assume you had some code that takes a file name and calls open on it. One day you decide you want to print that filename before you open it. Naive code will cause the name to “move” to print and unusable to the open in next line. Even though it is perfectly understood by all parties that there is no threading involved and print would finish before the next use of that string. Yes, I can create a borrow or clone, but having to think of it every single line of code even when there is only one thread of execution is really painful Edit: I get print is a macro, but imagine a detailed logger for this case.
- fasterthanlime 6y agoI'd argue the exact opposite. Languages that don't have a concept of ownership, and a borrow checker, and don't explicitly say if they want ownership, a reference, or a mutable reference, force you to keep all of these details in your head. Here, if I have a `&T` and I try to call a function that has a `&mut T`, the compiler will tell me that's not gonna work - and then I can pick whether I want my function to take a `&mut T`, or if I want to make a clone and modify that, etc. There's a learning curve, it's a set of habits to adopt, but once you embrace it it's really hard to go back to languages that don't have it! (See the rest of the comments for testimonials)
- globuous 6y agoExcept from that though, it really looks like ocaml, which really makes me want to look into rust a little bit more :)
- fasterthanlime 6y agoYup, strong similarities there, since the original Rust compiler was implemented in OCaml :)
- julenx 6y agoFor those who are curious, here's the source code of the original compiler written in OCaml (rustboot) https://github.com/rust-lang/rust/tree/ef75860a0a72f79f97216f8aaa5b388d98da6480/src/boot https://github.com/rust-lang/rust/tree/ef75860a0a72f79f97216...
- felipellrocha 6y agoYou get used to it, and you don't really need to use it most of the time. Only for more complicated things, but by that point, it's second nature.