> Why can't references be updated automatically when a vector is moved?

Try to think about what it would take to implement such a thing. How do you update references that are stored in registers? The stack? The heap? What if the references are self–referential? What if multiple threads have references to the same object? How do you ensure that all threads with a reference to the same object are all updated in sync with each other? Does your design require that every dereference go through a lock?

Where will this code live? It has to operate at run time, of course, but where does the compiler insert it? Does it get inserted into the vector methods, or into any method that might reallocate something? How does the compiler tell when that will happen?

Is it worth adding a runtime to Rust, when one of the key design decisions was to remove anything that required a runtime?

> I also don't get this whole complexity about string handling (e.g., string slices vs regular strings).

The big difference between a &str and a String is that the String has methods that can reallocate the backing buffer, while &str does not. The &str has just enough information to give you read–only access to a sequence of (utf–8 encoded) characters, but not enough to let you write to them (because writing might cause reallocating).

Think about what the compiler does to string literals. If you write "foo".to_string() in your code, the bytes 0x66 0x6F 0x6F have to live somewhere in the executable, and they have to be put into memory at an address the compiler can predict in advance. Typically that means the compiler will put them into the data segment of the executable. The program loader will copy that segment into memory, and then update the program’s global offset table using the starting memory address of the executable. Once that is done the program can actually be started, and the &str for the literal can be loaded out of the GOT.

Converting that to an owned String using the to_string() method has to copy from the data segment into a heap–allocated buffer, because the data segment is probably read only and anyway mutations might require reallocation (which cannot be done inside the data segment).

I suppose the Rust language could have been simplified by making the compiler automatically convert all string literals into Strings for you. This would reduce the number of things that need to be taught to new users, which is nice, but it would be rather unfortunate for performance in programs that don’t actually need to mutate those literals.

> The whole language just seems unnecessarily complicated, quite a contrast to the cleanness and elegance of Ocaml. Can't we have a simpler syntax and design layered on top, probably with a GC as an option?

Ocaml already exists. You can write programs in it if you want. In fact, I encourage it! Most programs should not be written in Rust, even though Rust has a lot of hype.

Languages like Ocaml get their cleanness and elegance by having a runtime that smooths over the complexities of the hardware in order to make the programmer as productive as possible. This is a good thing, because most programs do not require absolutely the best performance. Instead, most of the cost of most programs is the time and brain–power needed to write them. Thus, most programs should be written in a high–level language with garbage collection precisely because they minimize the amount of effort the programmer needs to use, maximizing productivity.

Rust is for those programs that benefit greatly from lower–level access to the hardware, and for which it is worth spending the extra time and brain–sweat on. The advantage of Rust is that C++ doesn’t have sum types, automatic closures, and memory safety.

Rust is an upgrade when the alternative is C or C++, but a downgrade when you could just write your program in Ocaml or Lisp or Javascript.