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.
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.
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.
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.
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
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.
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.
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... )
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'm worried that all of us are waiting for something to replace C++ that can't happen.
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).
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.
string.as_slice() == "foo"
"Parsing" command line args (it's not as slick as it could be, but it's not entirely horrible... for me as an experienced Rust user anyway): match std::os::args().as_slice() {
[] => println!("need more arguments"),
[ref x, ..] if x.as_slice() == "-h" => print_help(),
[ref a, ref b] if a.as_slice() == "-f" => write_to_file(b.as_slice())
args @ _ => println!("unrecognised arguments: {}", args)
}
Of course, using a proper parser like getopts or docopt.rs would be better:- http://doc.rust-lang.org/master/getopts/ - https://github.com/BurntSushi/docopt.rs
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).
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).
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.
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.
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
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.
Strings got re-done like ~2 months ago, so don't feel too bad ;)