What kind of pitfalls can you fall in when designing a software using C or C++
Safety
There are no guarantees about the code.
A wide range of unsafe practices are permitted.
Writing safe code requires more effort than writing unsafe code.
API
This depends on whether you're comparing C or C++.
C
In C, you can write code that exposes a public API which can be consumed by any programming language. However, the language is unaware of modern concepts that would make it easier to write efficient software with flexible APIs. The lack of generics and algebraic data types, for example.
C++
In C++, you do have access to templates, but the language is still deeply rooted in C idioms. To take advantage of newer language concepts, you need to be aware of, and expend more effort to write your APIs around them. It's not going to be an elegant solution. It also lacks a package manager.
Rust could allow you to get a better architecture
Safety
The language provides static guarantees about the code.
Unsafe practices are prohibited outside of an unsafe scope.
Writing unsafe code requires more effort than writing safe code.
API
Since Rust is a newer language that hasn't ignored past research, it does ship with modern language concepts as part of the core language. They're very easy to use, and deeply encouraged / required. It doesn't have the legacy cruft that C++ suffers from. Cargo makes distributing and retrieving dependencies simple, as well.
One of the neat things is that we can guarantee that you'll be able to compile a project from 10 / 20 years ago with the exact dependencies as the project originally required, due to the immutability of Crates.io. Rust is not yet old enough to test that claim, but it's theoretically possible due to how the Cargo.lock file works. I think this is critical for certain applications which require it (ie: scientific resarch).
How can Rust allow you to build a safer software while maintaining a clean API?
This is somewhat answered already. The borrow checker can ensure that the API is handling memory safely, as well as ensuring that the caller is using the API correctly. The type system can additionally be used to ensure that only certain methods are available for specific inputs, such as using a newtype to wrap a type and exposing methods that can only be used with that newtype.
As for a clean API, this is merely made possible through updated language concepts, such as algebraic data types and generics. Try comparing union types in C to enums in Rust, then imagine building an API around them and forcing callers to use that.
Rust may not be the best answer for anything
I think Rust can be great for everything, eventually. The language is less important than the ecosystem. Yet Rust has a great language that could enable a stronger ecosystem that's easier to work with. Though I think Julia has a place among scientific research and teaching basic programming, whereas Rust would be great for writing libraries for Julia to interact with.
Would you happen to know what part would be a good candidate?
At System76, we use it for all of our system-critical infrastructure. From kernels to system services. We've found it to be good for writing desktop applications for Linux with GTK, too. If you want a project to have a high chance of success, you'll likely want to use Rust. Requires that you own some experienced Rust programmers, though, of course.
Do you know specific patterns that are error prone in C/C++ but easily avoided / made safe in Rust.
I think this is best answered by reading up on the research papers that Rust was based on. The Cyclone language research project, for example. As mentioned on the website, perhaps the most critical pattern that Rust solves is the ability to write highly multi-threaded software that shares and manipulates data across threads, safely. Solving that problem solves many other problems. Yet it also solves a lot of other issues, such as constructing types that we can store references which are guaranteed to remain valid.
One thing I think is interesting is that Rust makes working an entity-component systems easier, due to generics. This can solve a lot of the same problems that object-oriented programming solves, but with more efficiency and less room for error. Due to the static guarantees, generational arenas and ECS is often a good solution for a wide variety of problems where you may want to store a lot of entities that may reference each other, or to have the ability to add and remove components from an entity at runtime.
I’m also interested in anything besides these that you consider especially important in Rust.
Rust, as a language, is really powerful. Yet that is only part of the equation. Perhaps even more important is Cargo.
Learning to write software in Rust is simple because building a Rust project just requires typing cargo build
. Dependencies are fetched automatically via Cargo before compiling, and will always be available, which will make Rust a huge success for students in schools. Newcomers will find it easy to locate libraries on Crates.io, search documentation on them from Docs.rs, and to publish crates of their own with Cargo.
The crate ecosystem also enables for rapid development and distribution of libraries for Rust. It's easy to publish a crate, and to continue to iterate upon it. NPM allowed for JS development to explode in popularity. Cargo can do the same for Rust. This is something that neither C or C++ have a parallel to.