I want a language I would enjoy programming in. Rust seems to have a lot of what I want (GC not mandatory, perf, etc, etc) BUT private-by-default is a terrible idea. I started writing a Rust program and I was just typing pub pub pub all over the place. Ugly. That plus the excessive markup the language requires for safety are enough to put me off it.
Video game programmers desperately need a new language to replace C++, but I think this is not it, because the amount of friction added for safety reasons feels very high.
I'll keep an eye on the language as it evolves, though.
The intent of private-by-default is to better control the API that a compilation unit presents. Rust allows you to inline functions across compilation units, which is incredible for optimization but inflates binaries with AST metadata (and as a result, inflates compilation times) if you attempt to make public every function under the sun. Rust also intends to have an ironclad versioning story, which means that API breakage will be taken tremendously seriously. With public-by-default, it's far too easy to accidentally break your API by modifying something that was not intended to be exposed in the first place. It's the antithesis of reliable software.
You're of course welcome to think that private-by-default is a terrible idea, but let's be aware that features are not adopted without reason. Programming language discourse (in general, not just related to Rust) is all too often dominated by people who seem to think that particular combinations of features are chosen at random out of a hat.
IMO the problem is not private-by-default but small modules and the confusion of scope with packages. (I should say that my Rust experience is non-existent so correct me if my intuition on its semantics is wrong).
It's reasonable that individual Rust files have their own scope. But it is a nonsense to imbue a namespace with an API or versioning story. Programmers do not think that way.
What has an API? A package. Something big enough that you can submit it to a package manager.
Therefore, private symbols in a package should be public to that package. That doesn't mean they should be in scope, but they should be available to be imported by other files in the package.
Public/private would then only be relevant at the boundary between packages.
There's a good reason to do this: it encourages modular design. A small-module language like Python uses modules so much that they miss the opportunity to take on a life of its own.
For instance, Django is split into hundreds of tiny "modules". But no one of them is big enough to be meaningful without the others. There is no singular "Django templates" module, nor a "Django ORM" module. In that sense it is monolithic via over-modularization.
That's a very interesting point of view, but it doesn't quite mesh with my view of the world. I think it makes perfect sense for modules much smaller than "packages big enough to be submitted to a package manager" to have small, well-defined APIs. I'm not sure why thoughtfully limiting APIs would be a good thing at the package level but a bad thing at the module level.
Seems like there's a good case to be made for a 3rd level of visibility between private and public. Similar to Java's package level. I'd like to see (from least to most public):
private: only visible inside the current namespace/module
crate (possible name): importable anywhere in the current crate/compilation unit
public: visible outside the crate/compilation unit
I think the problem is not private-by-default, but the repetition of "pub". We tried public-by-default and it led to a situation where it was very hard to make a stable API for your modules (which we need to avoid breakage of existing code). It could be solved with a "pub { ... }" block (which I'm pretty sure you could write today with a macro if you wanted to). Personally, I prefer repetition of "pub", as it forces me to think about what API I want to present to consumers of my library, but I can see how it can be annoying, especially if your modules are purely for internal use and you aren't concerned about API stability as much.
Regarding safety annotations, there is a WIP effort to eliminate 80%-90% of the lifetime annotations used in practice. There are also a couple of proposals to change the syntax of lifetime annotations to be less noisy.
I certainly welcome the attempts to reduce annotations and will look again at the language in a while.
It is interesting that the design priorities are so library-centric. I agree that library quality is important, but think about this: For every library that someone writes, N people are going to use that library in leaf programs, where if the library is useful N is much much greater than 1. So by definition the vast majority of code is not library code.
In video games we write programs that are between 200k and 2M lines, say. That is big enough that you do want to think about what API you are presenting from part of that program to another, but stability of that (internal) API is almost never a concern, and in fact this is one of the big boons of statically-typed languages: you can change a function and you instantly get errors at all the calling sites, allowing you to be sure you updated them (except in some bad ambiguous situations).
This fluid ability to change how parts of your program talk to each other is very important in large programs. It is one of the major ways you avoid code rot. The more friction is introduced into this process, the lower the ceiling on how large and complex a program you can build.
The other thing about games is that the problems are inherently very interconnected. Yes, we kind of partition things into different modules, but these modules all want to talk to each other very heavily (partly for inherent problem space reasons, partly for performance reasons). So again, friction introduced here hurts us more than it hurts most people.
I understand that private-by-default seems like the right idea because it supports a certain paradigm of programming that is generally considered the right thing (encapsulate your implementation details, modularize as much as possible, etc). But what I have found, in my field at least, is that this is less true than many people think. Yes, it's important to keep things clean, but fluid ability to change code is very important, and overmodularization hurts this. I think that some day this will become more widely understood, the same way that today most good programmers understand that OOP is not some kind of One True Panacea the way folks in the 90s seemed to think.
Good ideas can be carried too far and I think for my field private-by-default is way too far. Having to put 'pub' on every member of every struct and on well more than half my functions is kind of bananas.
(As someone who does not buy into the paradigm of OOP, I want to write functions that operate on data. In C++ sometimes I encapsulate these functions as struct members but this is just an aid to understanding when it is convenient; most functions are declared at file scope. In order to have a functions-operating-on-data mentality in Rust, I guess I need to put a pub in front of every member of every struct, which feels very distasteful; it feels like the language is pushing me toward paradigms that many in my field have tried and subsequently rejected after decades of pain.)
Well, this certainly went a few places, but that is where I am on these issues.
I guess the way I see it is that there's a difference between a namespace and a module. A namespace is what you're describing: you want a way to isolate the symbols of different components from one another, but you don't really want ironclad abstraction barriers. A module, on the other hand, is the unit where you really care about presenting a well-defined API. Sometimes projects call for one or the other, or a mix of both. You can always technically model namespaces as modules, but you end up repeating "pub", "public", or "export", or whatever your language calls it a lot. So it may well be good to have both features in the language.
Yeah, other game developers have requested that the safety guarantees be relaxed, because they don't particularly need it and relaxing them would allow for "nicer" code.
This probably just means that Rust isn't appropriate for that domain at the moment. The focus is strongly on memory safety, as that's what security-critical applications need: compromising this may weaken the ability for Rust to overcome C++'s inertia in the space of web browsers and operating systems (etc.), because zero-overhead memory safety enforced by the compiler (not just by convention) is the big draw-card.
Maybe in a world of web-connected multiplayer games handling logins and payments, security will become more attractive; or maybe the fewer memory corruption (etc.) bugs will eventually win the gamedev community over. (I know some gamedevs have been surprised that they have somehow managed to have very few bugs in their Rust code: http://www.reddit.com/r/rust/comments/28mlba/what_is_your_bu... )
I've been watching Rust for a while from the game development perspective and as much as I want it to be, I don't think it's a great fit for that domain. Still rooting for Rust because we really have a lot of work on the security front but I've been having a great deal of fun programming in Nimrod (http://nimrod-lang.org/). It's a bit of a one man show but there's a lot to like.
I really like both of them too. I really like Nimrod. The smaller community and (at least when I last checked) bad documentation especially for async stuff made me worry a bit.
However we should remember both languages are basically pre-release. They are not finished. That will take a while. After that it can take a while for a nice eco system and a common coding style to emerge. Not in the sense of hard rules, but when a community emerges around a language it's a bit like they develop a common accent and vocabulary. From that on other stuff develops, thickening the eco system fixing bugs, developing stable libraries. All of these things take and a lot of it is done by the first people getting into the language, even before it is released.
However I think it will still take a while of people trying to do something completely new with both languages to find and get rid of rough edges, while learning to use the language.
Look at Go for example. They enforce thing with fmt, etc. and it has been in use for a while now, but despite being used in production by many companies the ecosystem is still developing rapidly, there are still known problems to be fixed in future releases, there are still completely new things done with Go, the community didn't really "settle" (of course never happens completely, else there would be stagnation) about certain things, there is (IMO) still no really good IDE, there is still a lot to be made and most importantly there are no "Gurus" yet. Okay, maybe you wouldn't call them gurus, but there is still a hype phase. And if you look back to slightly older languages. Node.js still has a lot of those problems too, but the hype is slowly going away (other than on fronts, like Meteor, etc.), making many things way more rational. This means certain people move away from it, but others are just starting to actually consider it, because things are in place.
You can also look way back. C and C++ have a huge eco system, Perl has a module system where each library you make publicly available on CPAN is tested across a lot of versions, against tons of platforms. Well, you might think about the language in one way or the other, but that are the kind of projects that you really want if you think about using a language in production/enterprise.
In the startup world things are a bit different, cause you often are okay to take certain risks in exchange for development speed for example. Of course that really depends on what you are doing, but I guess it is hard to deny that a lot of (successful and not so successful) startups base upon very new, cutting or even bleeding edge technologies, while older or enterprise companies tend to be way more conservative about that.
I hope that doesn't sound bad, but the defaults in Rust are a really good thing in my opinion, cause it kinda pushes you into having better programming styles. In theory this could be done by a lot of experience self discipline, but in reality that can be hard hard to get, especially in a team.
I think your response is on point because Rust also seems to be a lot of what I want (GC not mandatory, perf, etc, etc, plus pattern matching). Private by default is literally a huge positive for me. But the terribleness of Rust strings is enough to put me right off of it.
I'm worried that all of us are waiting for something to replace C++ that can't happen.
When you say "terrible", are you complaining about having to write `.as_slice()` and `.to_string()` all the time?
For context for others who may not have a lot of Rust experience, Rust has two string types, the heap allocated `String` and the 'string slice' `&str`, which is a view of some string data anywhere in memory (possibly on the heap, possibly not). A little more info about these types: http://stackoverflow.com/a/24159933/1256624
At the moment, conversions between them are via explicit method calls, which can result in verbose code when handling them. An explicit conversion is required even for the super-cheap `String` -> `&str` conversion (which is just repackaging the 3 words (length, capacity, pointer) of String into the 2 words (length, pointer) of the &str).
That is part of the problem. The bigger part of the problem is comparing String's with constants. It simply doesn't work easily.
Also, start trying to do pattern matching on Vec<String> vs Vec<str> (and the wide variety of static, ref, etc versions there in) and you end up spending a lot of effort to do what is very basic in most languages that support generic pattern matching.
Try a simple use case, parse a command line argument array via pattern matching in a Rust program.
I think it gets smoother with practice (as in, with practice you're able to know intuitively when you need .as_slice and when a `ref` is required etc.), but yes, improving the ergonomics will be nice. Dynamically sized types will allow for String to be treated implicitly as a &str in many cases, so, e.g., my_string.trim() will work (it currently requires `my_string.as_slice().trim()`). And there is some loose talk about overloading pattern matching via the same mechanism, which would allow proper pattern matching on String with literals `"..."`.
> support generic pattern matching.
There's some subtlety here, e.g. Haskell doesn't support pattern matching on Data.Vector.Vector or Data.Text.Text (which are some of the closest equivalents to &[T], Vec<T> and String).
A) I understand how to make Rust do what I want with strings. B) Even with your examples, I think you've glossed over some of the even trivial use cases for dealing with Vec<String> vs Vec<~str> (or whatever the current nomenclature is). C) Even with your great comments you've pointed out that String manipulation in Rust is not "natural". Which is something that is a very high priority for me.
That said, easy string manipulation is a high priority feature for me. Non-private data access is a high priority feature for the original commenter. I worry that too many of us have expectations for Rust that are unlikely to be fulfilled.
That's not to say that the Rust team has set too high of barriers, only that we have elevated them to something they aren't. I'm less concerned with what the Rust team is delivering (which so far has been great) than with what people seem to be expecting (which is nothing less than a C++ replacement in all contexts).
The problem is that the string story is on two sides of a barrier erected in Rust very much by intention: To make memory allocation explicit, and to enable allocation-free operations as far as possible
This is why Rust will have you juggle String (a string with allocation) and &str (a "string slice"; a view into an allocation elsewhere) all the time.
> I'm worried that all of us are waiting for something to replace C++ that can't happen.
From the early days of computing, history proves systems programming languages only get replaced when OS vendors push another language on their official tooling.
Apple is now pushing Swift and Microsoft, at very least, dumping C.
So will Mozilla push Rust as part of Firefox OS? If not, which OS vendor will pick it up?
Note this applies also to other "wannabe C++" replacements.
Rust looks very interesting for the embedded programmer. Imagine a leightweight OS with minimal runtime requirements, similar to Contiki[1], but done right.
Sorry, that is a bad reading of my comment (which is my fault). Rust strings are in no way preferable to C++ strings (and in some ways worse). Which is a major failing of Rust FOR MY USE CASES.
That is to say, I had huge expectations for Rust, that it did not (yet) meet. It really made me think about what I wanted from a systems language, and what is possible.
Please don't read this as a screed against the future of Rust, just the present, which didn't really enthrall me enough to learn more of vs C++11
Rust strings are in no way preferable to C++ strings
I haven't been following Rust closely, so this is the first I've heard about the string/slice distinction. But I have spent a lot of time trying to remove unnecessary allocations/deallocations/copies from performance-critical, string-heavy C++ code. I use a combination of custom allocators and slice-like objects, but both of these approaches make it a hassle to interact with code using the natural std::string.
A pervasive distinction between strings that manage their own memory and those that don't, along with language and library support to make using them in combination straightforward would be a big win for me.
I think having felt pain with std::string, you would implicitly understand and enjoy working with the two string types in Rust. They do everything it sounds like you've been doing manually and inconveniently in your C++ code.
Agreed. I'd also make an analogy to C++ where you sometimes must hop between std::string and char*'s with ambiguous lifetimes. Not to mention various custom string types that any big app will have (ex. one that internally exploits jemalloc's allocator optimizations (i.e. exposing block overages), or does rope-style allocation, or intern's into a shared table, or whatever).
I ask because I want to know what Rust's pain points are so that I can suggest ways for the devteam to do better. If you have any feedback, please make your voice heard while we still have time to consider it! :)
The history of gamedev (in particular) eschewing C++ for years until the consensus flipped gives me hope that something similar might happen here. The strings thing may take a similar path to so many aspects of C++ -- where it took years of folks like Scott Meyers and Josuttis and Herb Sutter and Alexandrescu talking through the details and rationale before enough people grew to like the decisions C++ made. Think of how many C++ programmers had to get their head around (say) RAII -- it probably first seemed horribly odd and weird, and now it's seen as a fundamental reason why it's a great language.
Video game programmers desperately need a new language to replace C++, but I think this is not it, because the amount of friction added for safety reasons feels very high.
I'll keep an eye on the language as it evolves, though.