It can. You just need to avoid those performance optimizations that were en vogue in the 90s/early00s. Luckily modern computers (and the more modern C++ standards) can handle value semantics just fine and you don't need to put pointers into vectors anymore because directly putting the data there would be too costly, etc.
If you only use the new (C++11 up) smart pointers (avoiding new/delete) you've essentially achieved 80% of Rust's safety model. Add rigorous application of const correctness and you're good to go. Rust still adds a few important things but until its eco system is matured (and there is real tooling) modern C++ is a pretty good alternative.
auto p = std::make_unique<int>(5);
auto q = std::move(p);
std::cout << *p << std::endl;
Segfault using the “safe” C++ features. I'm a fan of modern C++, but it's not safe (in a Rust sense) even if you stick to C++11/C++14 features. fn main() {
let p = Box::new(5);
let q = p;
println!("{}", p);
}
gives, upon compiling: error: use of moved value: `p` [E0382]
println!("{}", p);
^
note: in this expansion of format_args!
note: in this expansion of print! (defined in <std macros>)
note: in this expansion of println! (defined in <std macros>)
help: run `rustc --explain E0382` to see a detailed explanation
note: `p` moved here because it has type `Box<i32>`, which is moved
by default
let q = p;
^
help: if you would like to borrow the value instead, use a `ref`
binding as shown:
let ref q = p;
error: aborting due to previous error
Running `rustc --explain E0382` or going to https://doc.rust-lang.org/nightly/error-index.html#E0382 gives you an extended error message as well.It doesn’t make sense to talk about C and C++ together like that and the idea that it would possibly maybe make sense to talk about two fairly different languages the same way is very much the root cause why some people consider it impossible to write “safe” C++. C and C++ probably have as much in common as C and Rust (both of these have pointers, so they must be the same) or as Haskell and Java (both of these have types/classes, so they must be the same).
I only mention those two together as the previous options for low level, no runtime languages. Outside of Rust both C and C++ were independent options I've considered investing more deeply in.
Wherever you look in the C/C++ world you find this legacy (POSIX, OpenGL, OpenMAX, OpenMP, OpenCL, on and on) so no matter how determined you are to try to match Rust with `safe' C++ you get dragged back into C. The term "C/C++" is used and understood because it is 100% appropriate, and it will continue to be appropriate as long as unsafe C/C++ interfaces proliferate.
Obviously Rust can't completely solve this problem either, but Rust at least provides a mechanism to isolate the C legacy parts behind tested and supposedly `safe' API interfaces, leaving the developer exclusively within his type+memory+data race safe environment. That is a superior model.
Did you consider either of those? Were your reasons for preferring Rust similar to what I put in the article?
let (|>) x f = f x
That's literally function application, in reverse. That's all. It comes in handy for type inference and to make things look nice, but isn't special in any way. I mention this as one comment as to why Rust doesn't have such an operator was that "Rust uses channels for data flow" which is a non-sequiter. Rust just doesn't value conciseness as much, and I'm guessing the safety/perf restrictions might complicate matters.In fact, even the + and - operators are implemented in F# and shouldn't count as part of the language (just very commonly used bits of the stdlib). I fear that perhaps not enough intros to F# focus on the relatively simple underlying language, and that everything else on top is just an application of said language.
With the type system, it's actually easy to figure out the operators, based on the type alone. Looking at |> for instance, we see a type of:
'a -> ('a -> 'b) -> 'b
This makes it entirely obvious. There's only one implementation for such a type definition. This holds true for so much of the stdlib that the "noise" quickly fades away. This is relevant as magic cannot hide, and misuse will quickly be identified by the compiler.But it's a valuable comment as it illustrates that the simplicity is not being marketed correctly and turning away potential users.
OCaml and Haskell have somewhat odd syntax, for someone who is coming from C like languages or even Python. You need to learn a bunch of functional programming concepts to use them effectively. You are still limited in what you can do, such as writing a native extension module for your existing Python code base. And they don't really help on one of the major selling points of Rust, which is to be able to write safe multi-threaded code with shared, mutable data structures. While there may be lots of good high-level parallelism available, at some point for certain problems you really just want to be able to use a shared, mutable data structure, but doing so in almost any language but Rust makes it very easy to make a mistake and have aliased, mutable references to it that can invalidate all of your logic, even if it isn't actually memory unsafe.
In Haskell you get correct by default and have to work for speed. In C your program is fast by default, but wrong, and you have to work for correctness.
There's plenty of evidence it can, for certain values of "program". redis comes to mind as a product with a specific goal and implementation, and well managed design process that makes that happen.
The problem is when projects grow to have dozens of developers, of varying skill, pumping in features in a continual battle to stay up to date with standards - like a web browser. Then, I think the possibility of secure C is shown to approach 0.
I'll admit that OCaml is something that I've looked at before but it didn't have the staying power for me. It's likely just because it was too different at the time compared to what I was used to.
As to how much the newer features help, time will tell...
Take a look at Herb Sutter's CppCon talk this year:
https://www.youtube.com/watch?v=hEx5DNLWGgA
Reinventing the wheel isn't entirely necessary. Though if you're a SysAdmin and you write your codebase in Rust.. that's some nice job security. ;)
And I object to using Rust (or other production-level but "edge" languages) as being "job security". If the underlying software matters enough that using Rust has significant benefits, then it matters enough that employees spend a few days learning a bit of Rust. This fear of languages as if learning a language was this huge mountain to scale in hiring is just silly. The actual code and environment probably outweighs the language used significantly.
The real exception to this is if you're running a codebase that has essentially non-programmers adding bits here and there. Somewhere where you know that the individual contributors will find learning a language to be a higher barrier than contributing new pieces of code. (So either a rather trivial system, or contributors that have an unusually low capacity.)
Learning a language in days is a fairytale, the fact that you're so cavalier about this point makes it seem that you're more interested in doing advocacy than having an honest discussion.
So in the context of a conversation about the costs of training and long-term maintainability, well, you should probably write all your stuff in Java, but if you can't, then you're better off selecting Rust over C++. I can teach Rust to anyone that knows Python, and once you know Rust you're at least familiar with the most useful subset of C++.
I gather from this post that you seem to use a risky strategy for your development - let juniors loose on a problem and hope that Rust/Java will save them. Or is that just a straw man? In any case, all juniors should get coaching, CI with static analysis and code reviews are likewise highly recommended.
Finally, I can't understand how someone can recommend Rust over C++ for long-term maintainability with a straight face.
The first stable version of Rust was announced four months ago, let's talk again in 10 years and see how your code from 2015 is doing.
Therefore I think it's quite fine to say it takes a few days to learn a bit of a language, enough to start contributing. Enough that it's not "job security" to pick something that provides a real benefit.
I think C++ has a lot of awesome stuff coming out but I find Rust more pleasant to code in. Also having package/dependency management has been great compared to using the old versions of libraries that come with distros or tracking down all the transitive dependencies of software. Rust is just a more delightful experience out of the box.
> Though if you're a SysAdmin and you write your codebase in Rust.. that's some nice job security. ;)
I definitely understand this but I don't think it's true and will be less and less true over time. It is becoming very common in the SRE/SysAdmin industry that strong coding skills is a requirement. When I started 10 years ago that was very different but it's becoming harder and harder to find jobs as a Linux grunt. The SREs I work with care about software engineering best practices, code review, testing, continuous integration, etc.
Also, even if that were true, I'd much rather have people with a weaker understanding contribute to Rust where the compiler has your back. If you haven't used Rust you might be surprised how good the error messages are. Once you spend a bit of time understanding the basics the compiler is super helpful.
- If you enforce memory safety to all of your code, the number of the libraries that you can use will be severely limited. Imagine if you can only use C++14 libraries. How many C++14 libraries are out there? And I'm not sure if there will be many libraries written in safe C++, at least for the next few years. The adoption rate for newer C++ standards (C++11, C++14) was generally not satisfactory.
- If you only enforce memory safety to your own code, then you could use the existing ecosystem. But in that case, your program will still segfault because of the libraries you're using. Admittedly this is also a concern of Rust when you use C libraries in Rust. The more safe code you use, the safer your program is. The problem is, if you choose this route, there's not much ecosystem-ish advantage in the C++'s side. You will also have to "reinvent the wheel" as you would have to do in Rust.
This problem all boils to this: adding guaranteed memory safety to the unsafe language is a breaking change. And the problems occurred by breaking changes are very hard to solve.
These are valid concerns, but you're being very dismissive of the benefits
I think confining errors to libraries is generally a good strategy. In effect you are guaranteeing that you're not going to introduce any new unsafe code.
It might uncover errors in the existing code.. but that's a much smaller issue and one that fades with time (esp if you're talking about libraries that a lot of people are using)
Is this "good enough" for a brand new aircraft-control/heart-lung-machine system? Maybe not. Maybe then you should start from the ground up using either Rust or C++/GSL entirely. But I think for most use cases this is sufficient and brings huge immediate benefits now. And of course with time libraries will be updated to include safety guarantees (which for the most part seem like it wouldn't involve a ton of work)
This is more of an organic process that basically in some form preserves the millions of man hours people have invested in C++ and less of a clean slate solution. As I've said before, you gotta make a living now in the current reality with the current codebases and the current tools. You can't just judge languages in a vaccuum.
This is probably the reason why our opinions vary. I believe rewriting existing libraries to be safe would be a tremendous amount of work. This estimation comes from my experiences with Rust. Safety guarantee hugely affects the interfaces of the libraries. You cannot "just" gradually increase safety of your library. It is basically a redesign. At least with what Rust currently provides this was my experience. And the method suggested by Herb Sutter seems to be very similar to Rust's. So I expect the same churn to happen in the C++ ecosystem, and it would effectively split the ecosystem into two: safe ones and unsafe ones.
Let me rephrase myself again: if adding safety to C++ in a gradual manner was that easy, Rust would have not been invented in the first place. The Mozilla guys could just use C++. There's a reason why Rust had to exist, despite all the "reinventing the wheel".
Don't diminish it, b/c the presented solution is non obvious/trivial. If it was, it would have been done ages ago.
So either A - The Mozilla folks may have not thought of this solution
B - At the time of Rust's development C++ was stagnant and not evolving as it is right now. There wasn't a lot of hope for fixing it at the time. It's thanks to the work of several people that the C++ standards committee now is a fast moving organization.
If Mozilla were considering starting the Rust project in 2016 I don't think they would have gone ahead with it
As an SRE/SysAdmin I struggle to believe that C or C++ can be written safely. It is very common that I see software being emergency patched because of memory unsafety from very experienced engineers in ubiquitous software.
Why would I need the performance/efficiency of C in the first place? Well, I regularly write software that runs on thousands to tens of thousands of machines in production. This software runs below the service. I feel a certain sense of pride in minimizing my observer effect on the system. With Rust, compared to Python, I can write software that has more functionality with a fraction of the memory and CPU utilization.
Another thing that has become more appealing to me is the strong static type system. Not having a formal background in CS I fell in love with Python for how it enabled me to move quickly, and Python is still my go-to language for most things, but after years of supporting large production sites plus running my own code in production I'm a bit tired of runtime exceptions. We have great exception logging and there are entire classes of bugs we'd just never see with Rust's type system and error handling.
Essentially, Rust gives me new super powers and the borrow checker is my sidekick making sure I don't shoot myself in the foot.