So, for example, it's really natural to do complex parsing with multiple layers without allocating any new strings but rather taking views into views into views. It's not something weird but rather the most natural and easiest approach. If you want to allocate, that's explicit.
Conversation
It's more like C than C++ in terms of copying and allocation. Assigning, passing parameters and returning is always a shallow copy like C. If the type has a destructor, that's a move of ownership. There is no move constructor concept, etc. though and it just works like C values.
1
2
References are also more like C pointers (but safe and non-null) than C++ references. They're simply regular values / types. &[T] and &str (slices) are also just a case of &T. Box<[T]> (rarely used) can be converted to/from Vec<T> (converting from it drops excess capacity first).
1
2
Most functions working with strings use them via &str, and similar with arrays. Rust's String is like std::string and Vec<T> is like std::vector<T>. They are far more interoperable with each other, references and smart pointers though.
1
2
So, for example, you can go from Box<[u8]> or Vec<[u8]> to String without allocation. It just has to check that it's valid UTF-8 and can make it a String.
You usually write code via references because it's simpler and reusable. Similarly, slices, not references to String, etc.
1
2
You wouldn't take references to String (&String) because then that doesn't work directly with string constants or views. You build around references/slices whenever possible, and then that just ends up naturally chaining together with a lot less allocation than you do in C++.
1
2
An API to parse a number from a string will naturally take a &str parameter. So, it's natural to be passing in a view to that. It doesn't care if the &str points into a String, a string constant (&str with static lifetime) or where it came from (multiple layers of slicing, etc.).
1
2
Closures and trait objects fit into the same system. You usually use traits (type classes / interfaces) as bounds on the generics (you have to declare all requirements in the signature, unlike C++ templates) but you can also use those as objects. It works like [T] and str.
1
2
C++ standard library has a lot of these things but they don't really fit into a cohesive system and it all completely relies on the programmer to not violate the rules to maintain memory safety. It's largely the same kind of stuff though, and is all natural to a C++ programmer.
1
2
A C++ programmer isn't going to wonder why [T; N] (array), str/[T] (dynamically sized slice types only usable via pointer types) and Vec<T>/String all need to exist and it all fits together much better without the same cruft and duplication that exists in C++. It's simpler.
1
3
It's nice that [u8]/str share the same format with methods for efficient interoperability, along with Vec<T>/Box<[T]> and String/Vec<u8>, etc.
So, for example, neither of these needs to allocate:
doc.rust-lang.org/std/str/fn.fro &[u8] to &str
doc.rust-lang.org/std/string/str Vec<u8> to String
