# Keep Rust simple!
Rust doesn't have very many features. Here are 10 features rust does not have:
1. No named arguments
2. No default arguments
3. No ternary operator[^1]
4. No null
5. No exceptions
6. No function overloading
7. No inheritance
8. No constructors (in the C++ sense)
9. No variadic functions
10. No implicit type conversions.
It turns out you don't really strictly need any of this stuff. Nothing on this list allows you to do anything you couldn't do without it. It's all mere "convenience features". And the absence of these convenience features makes the language simpler. Simpler to learn and simpler to use.
Now let's take a look at some complexity that Rust does have: several different string types. The two main ones are:
1. `&str` which is a 'string reference'.
2. `String` which is an 'owned string'.
Okay, to someone from a normal programming language, this might seem weirdly overcomplicated. Why not just use Strings everywhere? I mean, it's not like there's a separate types for "owned integer" and "integer reference".
And I admit that there was probably a simpler design possible. But not one where you can just remove `&str` or `String` from the language and still do everything you could do before. This is not complexity added for convenience, it's complexity traded for power. What `&str` does is it allows you to get a reference to "just part of a string". This means you can refer to "part of a string" without making unnecessary copies:
```rust
fn get_first_word(s: &str) -> &str {
s.split_whitespace().next().unwrap_or("")
}
```
This is the type of complexity I'm okay with, and it's notable that nearly all of the complexity of rust is kind of like this. There is a little syntax sugar, like `if let` and `let else` and `?`, but it's all very "local". Adding default arguments would be "nonlocal", because someone's choice to add default arguments to their library would affect all the users of their library. On the other hand, someone's choice to use `let else` is not visible outside of the function it's used in.
Here is an example of an awesome nightly feature that I would use all the time if it were stable... that I don't think should be added. It's `default_field_values` and it allows you to do this:
```rust
struct Player {
name: String,
health: u8 = 255,
damage: u32 = 5
}
// Now this is valid:
// Player { name: "Nauseam".to_string() }
```
I see this and I feel tempted. It would be so convenient! (Especially in cases where you can't really use the spread operator because not all fields have sensible defaults.) But what's convenient for me might not be convenient for you. And if Rust added all the features that are convenient for anyone, eventually Rust would look like Python:
```python
def f(pos_only, /, standard, *, kw_only):
# Imagine being a python beginner encountering this syntax
pass
```
I'm picking on Python a bit here, but I feel like at this point Python has such a large surface area that there's no way that a beginner could possibly feel truly comfortable with it in a reasonable amount of time. (Sure, most beginners might not ever write this, but it's something they'll probably run into eventually and have to put in their "one more thing to remember" list.) If Rust added every feature people ask for, eventually it would look just like that.
So what is there for Rust to do? Lots! The best language changes are those that feel more like "lifting restrictions" than "adding features". Think about it like: are we adding a new thing to remember, or removing something to remember? Now you can use `async` in traits. It's a new feature for sure, but everyone who learns about traits and learns about async just assumes by default that you can use them together. Now you can (with caveats). Hopefully someday we'll have const in traits too. One day we'll get polonius, and the borrow checker will just suddenly be slightly more permissive and people will run into "borrow-checker issues" a little less. And so on.
## Tangent, but static analysis is also great for beginners
Take a look at this python code:
```python
x = [2, 1, 4]
x = x.sort();
print(x)
```
See the issue? Yeah, `x.sort()` will sort `x` in place, and return `None`. For this code to work, you need to write `x = x.sorted()`. Not the most complicated thing in the world, but I've helped multiple Python beginners with this exact issue. Let's see what would happen if we wrote this in Rust:
```rust
fn test() {
let x = vec![2,1,3];
let x = x.sort();
// error: cannot borrow `x` as mutable, as it is not declared as mutable
println!("{x:?}")
}
```
Yep, we get a compile error telling us that `.sort()` is going to mutate `x`. But let's say we power through and make it mutable:
```rust
fn test() {
let mut x = vec![2,1,3];
let x = x.sort();
println!("{x:?}")
}
```
Despite this being perfectly valid rust code, it's still obviously not the right thing to do. And rust tells us exactly what we did wrong!
```
warning: this let-binding has unit value
--> src/lib.rs:3:5
|
3 | let x = x.sort();
| ^^^^^^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
= note: `#[warn(clippy::let_unit_value)]` on by default
help: omit the `let` binding and replace variable usages with `()`
|
3 ~ x.sort();
4 ~ println!("{():?}")
|
```
Ok, that's not the most helpful possible suggestion, but I think this is way better than the situation in Python. I suspect that powerful static analysis that detects mistakes for you and explains them... is actually very convenient for beginners (who are liable to make a lot of mistakes).
[^1]: Because `if` is an expression, it can do everything the ternary operator would do, without introducing an extra concept to the language.