| Rust didn't invent any of these syntaxes. Pascal was invented around the same time as C and had functions that start with "function" and types that use the "binding: type" notation. And lots of languages (Ada and ML come to mind) followed in Pascal's footsteps that predate Rust. It has benefits. It's easier to parse for both humans and machines and it allows for easier type inference. >Closures use pipes instead of any of the existing syntaxes we're used to. Closures in Ruby use pipes. It's a common syntax. >Why couldn't they just use the familiar concept of classes instead of whatever is going on with their strange NIH traits? NIH describes the whole language, every little thing has to be different somehow. Needless to say, traits aren't NIH either, and there's good reasons for avoiding class-style polymorphism in a language like Rust. Here's some reading to help explain. https://stevedonovan.github.io/rust-gentle-intro/object-orie... It also happens to include this choice quote: >I once attended a Java user group meeting where James Gosling (Java's inventor) was the featured speaker. During the memorable Q&A session, someone asked him: "If you could do Java over again, what would you change?" "I'd leave out classes," he replied. After the laughter died down, he explained that the real problem wasn't classes per se, but rather implementation inheritance (the extends relationship). Interface inheritance (the implements relationship) is preferable. You should avoid implementation inheritance whenever possible And that's basically what Rust gives you |
For example, suppose you just invented the type Qux and you always know how turn any Baz into a Qux.
Rust's standard library provides four interesting conversion traits that may be applicable for different purposes, From, Into, TryFrom, and TryInto. Oh no, implementing all of them sounds like a lot of work and who knows which anybody would need?
Just implement From<Baz> for Qux
A blanket implemention of Into<U> for T when From<T> for U gets you Into, then a blanket implementation of TryFrom<T> for U when Into<U> for T gives you TryFrom, and finally a blanket implementation of TryInto<U> for T when TryFrom<T> for U gets you TryInto as well.
So you only wrote one implementation but anybody who needs any of these four conversions gets the one they needed.
Another more obvious example is the blanket implementation of IntoIterator for any Iterator. This makes it easy when you want to be passed a bunch of Stuff you're going to iterate over, just give a trait bound on your parameter saying it has to be IntoIterator for Stuff. You needn't care if the user of your function passes you an array, a container type, or an iterator, since all of them are IntoIterator.
It would have been so easy to overlook this, and end up with a language where people find themselves collecting up iterators into containers over and over to make more iterators, or else constantly turning containers into iterators to call functions.