> “[no other] viable high performance language with the same ecosystem, support and tooling”
This comment strikes me as deeply contradictory. I have qualms with C++ as any other C++ programmer, but saying it’s famous for being famous is just ridiculous.
C++ has been used to operate nuclear submarines, airplanes, pacemakers, financial systems, ... over the last few decades. I’d like to know what you consider “battle tested”.
I belive that if Java and .NET actually had a proper AOT toolchain since day one, instead of being commercial only (Java's case) or doing basic optimizations (NGEN), there would be even less use cases for its use.
EDIT: typo
Modern C++ is much better, unfortunately there's stuff from the past they can't ditch without breaking compatibility so it really isn't much better - yes it's got better tools, but nothing's going to make you use them unless your compiler has some clever analysis features you can turn on and do things like make raw "new" an error.
For me modern C++ is interesting, but it has even more of the problems that Rust has. There are lots of good ideas that help fix problems we've seen over the last 40 years or so (man, I'm old...), but I think unless you are already a C++ programmer it's probably more convenient to start with a language that was designed from the beginning to solve those problems. If someone paid me, I'd very happily write C++ code again, though.
Ha ha! Before I answer, I should say how much I appreciate the work you do. It really is awesome.
I've been a bit clumsy with my words, though. It's not so much that I know there is a more elegant way to do it as much as I'm left with that feeling. For example the ? operator. It's incredibly useful and makes the code much less clumsy. But I'm left thinking, "Why do I need this"?
I've got a list of actions, some of which could fail and some of which can not fail. I want to exit early if something fails. Is there another way I could represent this? For example, something like a "do" notation. Then each statement essentially needs to be a monad. I'm not necessarily keen on that, but I think we're missing an abstraction somewhere. The set of functions that all return the same type, describe a monad... Why am I mixing functions with different return types, I wonder...
The thing is, I've got this extra notation that I have to learn about that does something useful. It just appears to me that I wouldn't need that extra notation if I could abstract the execution differently. Maybe that's a pipe dream, but it gnaws at me :-) And, of course, if we had it, the result wouldn't be Rust -- you'd need a completely different language to support that way of looking at it. Just to get rid of ?. Yeah, not worth it for Rust, but I think still worth thinking about.
Sorry for the rambling nature of this post :-) Just to leave you with a more concrete example that's not about Rust. I actually like many things about Javascript. One of the things I found really elegant about JS is that there are no private or public variables. If you want something to be private, you just make a closure. There is no need for a "private" keyword or a "public" keyword. There is no need to build in special code in the interpreter. It just works because that's the way it works.
Another thing I found really elegant about JS (though, unfortunately not executed as far as they might have) is the way classes are represented. You write a function that returns a dictionary/struct. That's a constructor. But it's also pretty much everything you need in a class. A class is just a function. Although it's a bit bizarre, functions are also dictionary/structs, so if you need class functions, just add it to the function. I'm not so keen on how they implemented prototypical inheritance, but it was the 90's so I'll cut them some slack ;-)
So when I say "elegant", what I mean is that the form of the language dictates its function. With Rust, I find that we have quite a lot of stuff like the ? operator. It's super useful and leads to better code, but it's also kind of tacked on. If I were going to design a language that had similar utility, but didn't need the operator, what would it look like? Maybe there is no more elegant solution. However I kind of think that with another 5 or 10 years of work with Rust people will start seeing the kinds of workflow that Rust engenders and will probably discover abstractions that fits that workflow better -- much in the same way that Rust has done with C++.
It happens, it's just the internet. I have enough karma, haha.
> Before I answer, I should say how much I appreciate the work you do. It really is awesome.
Thanks! It's cool, I was just legitimately wondering. As you said sorta, it's helpful for me to understand people's perspective. If you'd like, I can give you some explanations here, but I wasn't asking to try and correct you, I was interested in your perspective. Thanks for sharing it :)
The hardest to maintain code is almost usually a mess of inheritance and generics/templates by someone far to "clever" that thinks OOP is the goal and not the tool. More often than not an inheritance hierarchy is basically (not literally) an abstraction over if/switch statements, except the logic will usually be spread out over multiple files and it's impossible to track all these potential branches in your head because they're purposefully obscured from you.
The OO style of programming also often moves the decision point. Instead of having an if statement above the 2 functions, you have the if statement at the construction of the object. If you happen to be writing code where you are doing mostly the same things, but certain details change in certain situations, you can group the differences into objects. If you are familiar with pattern lingo, this often happens if you are doing something that lends itself well to a template pattern. You can then use a bridge pattern to ensure that there is only ever 1 branch, instead of adding a decision point every time you want to vary the behaviour.
Where I find people (reasonably, IMHO) disagree is that having the decision point at the place where the behaviour diverges allows you to see the logic of the situation right at that point. If you have a bug in the code related to that, then it's very easy to figure out where it is, because all of the logic is local.
On the other hand, if you are refactoring the code, or changing the behaviour across the system, then it is very difficult to change because you have to search for all of those decision points. It's also harder to reason about on an architectural scale because it takes discipline (that may be lacking) to ensure that you always do things exactly the same way. You have a lot more flexibility because you can add all sorts of different logic to each decision point. The lack of constraint means that you have to inspect each case to determine if your overall change is going to work.
I'll be honest. I like OO. There are a lot of really diabolical ways you can write bad OO code, but there are lots of diabolical ways you can write any code. I'm familiar and comfortable with imperative, modular code. I'm familiar and comfortable with FP. I'm even familiar and comfortable with declarative code (I wrote some large XSLT systems). There are trade offs which every approach. It's easy to see problems in code that you are not happy with and then throw the baby out with the bath water. Having said that, I don't think there is a problem with having a preference of one style over another, but it's a good idea not to fool oneself into thinking that it solves every problem.
More often than not I find this isn't the case. If we have an inheritance tree of (A -> B -> C) then you'll run into cases where the bug is in a object of type C but it's calling a method defined on type A, overridden in type C and accessing a variable in type B. So you have to look at why A and C diverged, where the value is set in B, what else calls the method on A, etc. You have to know the whole graph and the abstractions are trying to hide it from you.
This is particularly true the older code gets because all those little "just overload this function" adds up and it can't be refactored because it's impossible to understand.
The very best programmers optimize their code for readability. Most of our day is spent reading code.
Sure, others could do the same, but they didnt.
Google, msft uses c++, c# and a bunch of others and they are probably the most complex software developers in the world.
It works.
Can it be better? Sure. Rust brings safety guarantees, f# enables functional programming mindset, julia is good for data processing. But oop did bring a lot of to market. We are evolving, and our languages will also evolve.