I'm a C++ systems programmer, so I'm trying to understand some of the issues here. I'm not familiar with either language, please forgive the ignorant questions.
Null: Why is null evil? Doesn't Go use it in the Java sense of releasing a reference to the object?
Global GC: Seems more like a runtime implementation choice?
Shared mutable state: Doesn't seem so bad for someone approaching a systems programming language. I don't always want to make copies and send it over as a message to the other threads?
My higher-level point is that, perhaps these languages could do a better job of selling, differentiating, explaining themselves. I know it's hard, that's why I'm trying to help by posing my stupid questions. Btw. if they're planning to write Firefox in this, maybe the author should maintain a minimal browser written in Rust to show how the language fits its original design goals.
Note that we don't forbid shared mutable state entirely (we don't forbid anything really, as we have an unsafe sublanguage that allows all the pointer tricks that you can do in C++), but we want to contain data races. Basically, as the FAQ says, "you can break the rules, if explicit about where and how".
We've learned that, especially when it comes to concurrency, there really is no one model that supports everything you might want to do. Instead, the idea is to support safe, data-race-free language constructs, and to make those the easiest ones to use. Data races (and memory unsafety) are clearly marked in "unsafe" blocks, with the idea that if you find a concurrency bug in your software, you can grep for that keyword and go over those blocks only, instead of having to crawl through the entire codebase.
The same principle applies to null. You can get nullable pointers (use option<T>), but you have to declare that that's what you want. You also have to declare some strategy for handling the null case every time you use the pointer, or at least communicate your intent to fail on null via the .get() method. The vast majority of pointers in any program aren't nullable, so in practice we've found that this eliminates a lot of bugs.
Null: Null is not evil, but programmers often fail to take it into account. If you see type `string`, it might not be immediately obvious that you can get either a `string` object, or `null`. It could be useful (for preventing bugs and for speed) to have different annotations for nullable and non-nullable variables/parameters (e.g. `string?`, or `string | null`, or `union(string, null)`).
Global GC: It's hard to do GC without stopping the world. If different threads have different heaps, only a single thread needs to be stopped at any given time for the GC to be performed (on this thread's heap). AFAIK, Rust also supports manually managed references.
Shared mutable state: I believe that is more of a language restriction, not VM (i.e. sending messages could be implemented without copying). Simplifies debugging and makes some abstractions much easier (distributed computing in particular).
Null is evil because it forces programmers to constantly check for null when compilers can do that much better. There can be situations when null is handy, but it shouldn't be the default. C++ reference can be used for similar purpose.
Global GC is definitely a language issue. It affects language semantics.
Avoiding shared mutable state does not mean making copies. If you can statically check there is no other reference you can avoid copying. Think advanced move semantics.
Null is evil because any pointer could be null, and the semantics of dereferencing a null pointer are either NullPointerException or just plain undefined.
While that is true, taking an initial look at the rust tutorial it seems to me that the number of syntactic rules in the language for Rust are much more than in Go, which is always a negative sign.
True, Rust has more syntactic rules than Go, but it's still (in my mind) a very small language, especially compared to C++. The developers are very conscious of language complexity and will not introduce new concepts spuriously. The increased complexity is because they're aiming Rust at a different application domain than Go, and for that application domain they feel that programmers require more fine-grained control.
Every language is small compared to C++. And keep in mind that Rust is still very young, and if simplicity is not a goal complexity can skyrocket before Rust reach 1.0 state.
However, the devs are aiming to keep the language simple. They're PL experts (watching them at work is quite intimidating, actually, as someone who doesn't have a PhD in CS), but they absolutely reject the allure of the ivory tower. Note the header on the page linked in this submission:
"a safe, concurrent, practical language"
Note "practical". I've witnessed several exchanges on the mailing list where the developers attempt to excise language features. For example, in Rust, `if` is an expression rather than a statement, and thus is redundant with the `?:` ternary conditional expression syntax familiar to C-like languages (the latter was removed). Alternatively, they often acknowledge that having three types of pointers is suboptimal from a simplicity perspective, but they consistently conclude that the semantics unique to each type of pointer are valuable enough to justify the mental cost.
Simplicity is definitely a goal. These days, not a day goes by on the Rust team in which we don't discuss ways to simplify the language.
My colleague Dave Herman always says that language design goes through cycles: You identify a problem, add solutions to fix it, then discover that these solutions are best merged with some other ideas. The result is that, as a language evolves, complexity goes up and down. I think that we're coming off a peak of complexity and the language is going to evolve toward simplicity in the near term.
Just to name a few simple examples, removing the alias analysis pass and removing "native modules" are some simplifications that mostly have consensus at this point.
One of Rust's primary design goals is safety. This is the source of most of its differences from both Go[1] and C++. Nullable pointers, free pointers, and shared mutable state are common sources of bugs and programmer errors in languages that have those features. Rust tries to address those issues by requiring the program to be explicit about handling such features. E.g., only pointers marked as nullable can have null value, and you are not allowed to dereference such pointers without a null check. This is the so-called "Bondage and Discipline" approach. There are pros and cons to this approach; the main arguments in favor of it revolve around the concepts of design-by-contract, and being able to enforce that contract, which lets you code less defensively.
[1] I get the feeling that Go's main design goal is ease of use, on par with Python.
Their goal of being a modern systems programming language are similar.