This discussion has focused on how Rust is not C, and doing things differently is bad.
Let's shift the focus to some things that are useful and convenient, and not just about safety guarantees that like anything safety-related are often less 'convenient', just like using your fall protection when working at height is less 'convenient' and a lot of people will be opposed to it, despite it saving many lives.
1. 'Enum Types' (aka. algebraic data types, sum types, tagged unions) and the match operator:
enum Message {
One(u32),
Two(String)
}
fn main() {
let foo: Message = Message::One(10);
let num = match foo {
Message::One(uint) => uint,
Message::Two(s) => s.len() as u32,
};
}
There is no equivalent of this in C or C++, and it is so useful and ergonomic. Match is also much more powerful than suggested here, it can include predicate conditions in addition to the demonstrated type matching, and of course classic 'switch/case' type conditions, etc. This is also used throughout core and std, for example the useful Result (returns an Ok type that wraps a result, or an Err type that wraps an error result) and Option patterns (return a wrapped type or None). Since this leverages the static type system, there is no storage cost.
2. 'Traits' (aka. interfaces, mixins)
// Adding to the above
impl ToString for Message {
fn to_string(&self) -> String {
match self {
Message::One(uint) => uint.to_string(),
Message::Two(s) => s.to_owned(),
}
}
}
// Then we can pass anything that implements ToString as a parameter as a virtual call:
fn stringable(foo: &impl ToString) {
println!("{}", foo.to_string());
}
// Or statically (generate new code for each T)
fn stringable2<T: ToString> (foo: T) {
println!("{}", foo.to_string());
}
There are a host of standard traits in the library, and they can be used for things like automatically parsing strings to other types, converting/coercing between types, operator overrides, comparisons, and many other common tasks.
You can do this in C++ with multiple inheritance, but it's a lot less clean and avoiding dynamic calls is a lot more difficult (maybe impossible? not a template expert).
3. First-class closures and good iterators/functional primitives
let pred = |x: &&u32| x < &&10; // Yes this looks a bit ugly, it's not as silly for non-primitive types
let data: [u32; 5] = [1,11,2,12,15];
let sum: u32 = data.iter().filter(pred).sum();
println!("{}", sum);
3
C++11 has closures, but they aren't as ergonomic and it's lacking a lot of functional methods. C++20 can do the above (I think), but it's not nearly as clean.
4. The 'question mark operator' / error handling
fn foo() -> Result<u32, MyError> {
let number = something_that_returns_a_result()?;
}
This is shorthand to unwrap the result if it's Ok, otherwise coerce to the error type of our result and return that. When you actually need to handle the error, you can use `match` or the similar `if let`. Not exceptions, and far more ergonmic than constantly wrapping calls in if() and returning sentinel values or whatever. C and C++ both suck at this and most programmers seem to prefer to not do any error handling at all.
And general observations: Type inference is awesome, and I haven't tried a language that is better than Rust at it (though I haven't ever ventured into things like Haskell). Rust's compiler errors are the most useful I have ever seen. The language seems to minimize boilerplate, while not generally feeling like it's constraining my style or forcing a particular approach. Learning how to contend with the borrow checker and lifetimes is challenging, but once you grok it, it doesn't really get in the way.
You adopted the philosophy of a servant. In your mind, there must be a master who guides you through everything. And you have chosen Rust to be your master. You perceive "not being allowed" as a good thing. This gives you a feeling of safety. You're told that there many problems. You have chosen to be afraid of them. You have chosen to trust your master to guide you around them in the safest way possible, whatever that means.
Wow, just...wow. What an absurd take. Rather, it is the opposite. I have taken the view that I'd rather let my tools do the hard work of figuring out if what I'm doing makes sense. I'm going to stop wasting my time responding to you.